1#!/usr/bin/env python 2# 3# Copyright (C) 2022 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Package <-> UID <-> Process mapper.""" 18 19import re 20 21# ex) Name: init 22PROC_STATUS_NAME_LINE = r"Name:\s+(\S+)" 23 24# ex) Pid: 1 25PROC_STATUS_PID_LINE = r"Pid:\s+([0-9]+)" 26 27# ex) Uid: 0 0 0 0 28PROC_STATUS_UID_LINE = r"Uid:\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)" 29 30# ex) package:com.google.android.car.uxr.sample uid:1000 31PACKAGE_UID_LINE = r"package:(\S+)\suid:([0-9]+)" 32 33USER_ID_OFFSET = 100000 34AID_APP_START = 10000 35UNKNOWN_UID = -1 36 37 38class UidInfo: 39 40 def __init__(self, uid, packageName=None): 41 self.uid = uid 42 self.packageName = packageName 43 44 def to_string(self): 45 appId = int(self.uid % USER_ID_OFFSET) 46 if self.uid == UNKNOWN_UID: 47 return "UID: UNKNOWN" 48 elif self.packageName is None and appId < AID_APP_START: 49 return "User ID: {}, Native service AID: {}".format( 50 int(self.uid / USER_ID_OFFSET), appId) 51 elif self.packageName is None: 52 return "User ID: {}, App ID: {}".format( 53 int(self.uid / USER_ID_OFFSET), appId) 54 else: 55 return "User ID: {}, Package name: {}".format( 56 int(self.uid / USER_ID_OFFSET), self.packageName) 57 58 59class UidProcessMapper: 60 61 def __init__(self): 62 self.nameReMatcher = re.compile(PROC_STATUS_NAME_LINE) 63 self.pidReMatcher = re.compile(PROC_STATUS_PID_LINE) 64 self.uidReMatcher = re.compile(PROC_STATUS_UID_LINE) 65 self.packageUidMatcher = re.compile(PACKAGE_UID_LINE) 66 self.uidByProcessDict = {} # Key: Process Name, Value: {PID: UID} 67 self.packageNameByAppId = {} # Key: App ID, Value: Package name 68 69 def parse_proc_status_dump(self, dump): 70 name, pid, uid = "", "", "" 71 72 for line in dump.split("\n"): 73 if line.startswith("Name:"): 74 name = self.match_re(self.nameReMatcher, line) 75 pid, uid = "", "" 76 elif line.startswith("Pid:"): 77 pid = self.match_re(self.pidReMatcher, line) 78 uid = "" 79 elif line.startswith("Uid:"): 80 uid = self.match_re(self.uidReMatcher, line) 81 if name != "" and pid != "" and uid != "": 82 self.add_mapping(name, int(pid), int(uid)) 83 name, pid, uid = "", "", "" 84 85 def parse_uid_package_dump(self, dump): 86 for line in dump.split("\n"): 87 if line == "": 88 continue 89 90 match = self.packageUidMatcher.match(line) 91 if (match): 92 packageName = match.group(1) 93 appId = int(match.group(2)) 94 if appId in self.packageNameByAppId: 95 self.packageNameByAppId[appId].add(packageName) 96 else: 97 self.packageNameByAppId[appId] = {packageName} 98 else: 99 print("'{}' line doesn't match '{}' regex".format( 100 line, self.packageUidMatcher)) 101 102 def match_re(self, reMatcher, line): 103 match = reMatcher.match(line) 104 if not match: 105 return "" 106 return match.group(1) 107 108 def add_mapping(self, name, pid, uid): 109 if name in self.uidByProcessDict: 110 self.uidByProcessDict[name][pid] = uid 111 else: 112 self.uidByProcessDict[name] = {pid: uid} 113 114 def get_uid(self, name, pid): 115 if name in self.uidByProcessDict: 116 if pid in self.uidByProcessDict[name]: 117 return self.uidByProcessDict[name][pid] 118 return UNKNOWN_UID 119 120 def get_uid_info(self, uid): 121 appId = uid % USER_ID_OFFSET 122 if appId in self.packageNameByAppId: 123 return UidInfo(uid, " | ".join(self.packageNameByAppId[appId])) 124 else: 125 return UidInfo(uid) 126