#!/usr/bin/env python # # Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """Package <-> UID <-> Process mapper.""" import re # ex) Name: init PROC_STATUS_NAME_LINE = r"Name:\s+(\S+)" # ex) Pid: 1 PROC_STATUS_PID_LINE = r"Pid:\s+([0-9]+)" # ex) Uid: 0 0 0 0 PROC_STATUS_UID_LINE = r"Uid:\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)" # ex) package:com.google.android.car.uxr.sample uid:1000 PACKAGE_UID_LINE = r"package:(\S+)\suid:([0-9]+)" USER_ID_OFFSET = 100000 AID_APP_START = 10000 UNKNOWN_UID = -1 class UidInfo: def __init__(self, uid, packageName=None): self.uid = uid self.packageName = packageName def to_string(self): appId = int(self.uid % USER_ID_OFFSET) if self.uid == UNKNOWN_UID: return "UID: UNKNOWN" elif self.packageName is None and appId < AID_APP_START: return "User ID: {}, Native service AID: {}".format( int(self.uid / USER_ID_OFFSET), appId) elif self.packageName is None: return "User ID: {}, App ID: {}".format( int(self.uid / USER_ID_OFFSET), appId) else: return "User ID: {}, Package name: {}".format( int(self.uid / USER_ID_OFFSET), self.packageName) class UidProcessMapper: def __init__(self): self.nameReMatcher = re.compile(PROC_STATUS_NAME_LINE) self.pidReMatcher = re.compile(PROC_STATUS_PID_LINE) self.uidReMatcher = re.compile(PROC_STATUS_UID_LINE) self.packageUidMatcher = re.compile(PACKAGE_UID_LINE) self.uidByProcessDict = {} # Key: Process Name, Value: {PID: UID} self.packageNameByAppId = {} # Key: App ID, Value: Package name def parse_proc_status_dump(self, dump): name, pid, uid = "", "", "" for line in dump.split("\n"): if line.startswith("Name:"): name = self.match_re(self.nameReMatcher, line) pid, uid = "", "" elif line.startswith("Pid:"): pid = self.match_re(self.pidReMatcher, line) uid = "" elif line.startswith("Uid:"): uid = self.match_re(self.uidReMatcher, line) if name != "" and pid != "" and uid != "": self.add_mapping(name, int(pid), int(uid)) name, pid, uid = "", "", "" def parse_uid_package_dump(self, dump): for line in dump.split("\n"): if line == "": continue match = self.packageUidMatcher.match(line) if (match): packageName = match.group(1) appId = int(match.group(2)) if appId in self.packageNameByAppId: self.packageNameByAppId[appId].add(packageName) else: self.packageNameByAppId[appId] = {packageName} else: print("'{}' line doesn't match '{}' regex".format( line, self.packageUidMatcher)) def match_re(self, reMatcher, line): match = reMatcher.match(line) if not match: return "" return match.group(1) def add_mapping(self, name, pid, uid): if name in self.uidByProcessDict: self.uidByProcessDict[name][pid] = uid else: self.uidByProcessDict[name] = {pid: uid} def get_uid(self, name, pid): if name in self.uidByProcessDict: if pid in self.uidByProcessDict[name]: return self.uidByProcessDict[name][pid] return UNKNOWN_UID def get_uid_info(self, uid): appId = uid % USER_ID_OFFSET if appId in self.packageNameByAppId: return UidInfo(uid, " | ".join(self.packageNameByAppId[appId])) else: return UidInfo(uid)