• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.companion.virtual;
18 
19 import android.content.Context;
20 import android.content.pm.PackageManager;
21 import android.os.Binder;
22 import android.os.Process;
23 import android.util.SparseArray;
24 
25 import java.io.PrintWriter;
26 import java.time.Instant;
27 import java.time.ZoneId;
28 import java.time.format.DateTimeFormatter;
29 import java.util.ArrayDeque;
30 
31 final class VirtualDeviceLog {
32     public static int TYPE_CREATED = 0x0;
33     public static int TYPE_CLOSED = 0x1;
34 
35     private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(
36             "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
37     private static final int MAX_ENTRIES = 16;
38 
39     private static final String VIRTUAL_DEVICE_OWNER_SYSTEM = "system";
40 
41     private final Context mContext;
42     private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>();
43 
VirtualDeviceLog(Context context)44     VirtualDeviceLog(Context context) {
45         mContext = context;
46     }
47 
logCreated(int deviceId, int ownerUid)48     void logCreated(int deviceId, int ownerUid) {
49         final long token = Binder.clearCallingIdentity();
50         try {
51             addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
52         } finally {
53             Binder.restoreCallingIdentity(token);
54         }
55     }
56 
logClosed(int deviceId, int ownerUid)57     void logClosed(int deviceId, int ownerUid) {
58         final long token = Binder.clearCallingIdentity();
59         try {
60             addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
61         } finally {
62             Binder.restoreCallingIdentity(token);
63         }
64     }
65 
addEntry(LogEntry entry)66     private void addEntry(LogEntry entry) {
67         mLogEntries.push(entry);
68         if (mLogEntries.size() > MAX_ENTRIES) {
69             mLogEntries.removeLast();
70         }
71     }
72 
dump(PrintWriter pw)73     void dump(PrintWriter pw) {
74         final long token = Binder.clearCallingIdentity();
75         try {
76             pw.println("VirtualDevice Log:");
77             UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
78                     mContext.getPackageManager());
79             for (LogEntry logEntry : mLogEntries) {
80                 logEntry.dump(pw, "  ", packageNameCache);
81 
82             }
83         } finally {
84             Binder.restoreCallingIdentity(token);
85         }
86     }
87 
88     static class LogEntry {
89         private final int mType;
90         private final int mDeviceId;
91         private final long mTimestamp;
92         private final int mOwnerUid;
93 
LogEntry(int type, int deviceId, long timestamp, int ownerUid)94         LogEntry(int type, int deviceId, long timestamp, int ownerUid) {
95             this.mType = type;
96             this.mDeviceId = deviceId;
97             this.mTimestamp = timestamp;
98             this.mOwnerUid = ownerUid;
99         }
100 
dump(PrintWriter pw, String prefix, UidToPackageNameCache packageNameCache)101         void dump(PrintWriter pw, String prefix, UidToPackageNameCache packageNameCache) {
102             StringBuilder sb = new StringBuilder(prefix);
103             sb.append(DATE_FORMAT.format(Instant.ofEpochMilli(mTimestamp)));
104             sb.append(" - ");
105             sb.append(mType == TYPE_CREATED ? "START" : "CLOSE");
106             sb.append(" Device ID: ");
107             sb.append(mDeviceId);
108             sb.append(" ");
109             sb.append(mOwnerUid);
110             sb.append(" (");
111             sb.append(packageNameCache.getPackageName(mOwnerUid));
112             sb.append(")");
113             pw.println(sb);
114         }
115     }
116 
117     private static class UidToPackageNameCache {
118         private final PackageManager mPackageManager;
119         private final SparseArray<String> mUidToPackagesCache = new SparseArray<>();
120 
UidToPackageNameCache(PackageManager packageManager)121         public UidToPackageNameCache(PackageManager packageManager) {
122             mPackageManager = packageManager;
123         }
124 
getPackageName(int ownerUid)125         String getPackageName(int ownerUid) {
126             String[] packages;
127             if (mUidToPackagesCache.contains(ownerUid)) {
128                 return mUidToPackagesCache.get(ownerUid);
129             } else if (ownerUid == Process.SYSTEM_UID) {
130                 return VIRTUAL_DEVICE_OWNER_SYSTEM;
131             } else {
132                 packages = mPackageManager.getPackagesForUid(ownerUid);
133                 String packageName = "";
134                 if (packages != null && packages.length > 0) {
135                     packageName = packages[0];
136                     if (packages.length > 1) {
137                         StringBuilder sb = new StringBuilder();
138                         sb.append(packageName)
139                                 .append(",...");
140                         packageName = sb.toString();
141                     }
142                 }
143                 mUidToPackagesCache.put(ownerUid, packageName);
144                 return packageName;
145             }
146         }
147     }
148 }
149