1 /*
2  * Copyright (C) 2011 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.settingslib.net;
18 
19 import android.app.AppGlobals;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.IPackageManager;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.content.pm.UserInfo;
27 import android.content.res.Resources;
28 import android.graphics.drawable.Drawable;
29 import android.net.TetheringManager;
30 import android.net.TrafficStats;
31 import android.os.Process;
32 import android.os.RemoteException;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.text.TextUtils;
36 import android.util.Log;
37 import android.util.SparseArray;
38 
39 import com.android.settingslib.R;
40 import com.android.settingslib.Utils;
41 
42 /**
43  * Return details about a specific UID, handling special cases like
44  * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
45  */
46 public class UidDetailProvider {
47     private static final String TAG = "DataUsage";
48     private final Context mContext;
49     private final SparseArray<UidDetail> mUidDetailCache;
50 
51     public static final int OTHER_USER_RANGE_START = -2000;
52 
buildKeyForUser(int userHandle)53     public static int buildKeyForUser(int userHandle) {
54         return OTHER_USER_RANGE_START - userHandle;
55     }
56 
isKeyForUser(int key)57     public static boolean isKeyForUser(int key) {
58         return key <= OTHER_USER_RANGE_START;
59     }
60 
getUserIdForKey(int key)61     public static int getUserIdForKey(int key) {
62         return OTHER_USER_RANGE_START - key;
63     }
64 
UidDetailProvider(Context context)65     public UidDetailProvider(Context context) {
66         mContext = context;
67         mUidDetailCache = new SparseArray<UidDetail>();
68     }
69 
clearCache()70     public void clearCache() {
71         synchronized (mUidDetailCache) {
72             mUidDetailCache.clear();
73         }
74     }
75 
76     /**
77      * Resolve best descriptive label for the given UID.
78      */
getUidDetail(int uid, boolean blocking)79     public UidDetail getUidDetail(int uid, boolean blocking) {
80         UidDetail detail;
81 
82         synchronized (mUidDetailCache) {
83             detail = mUidDetailCache.get(uid);
84         }
85 
86         if (detail != null) {
87             return detail;
88         } else if (!blocking) {
89             return null;
90         }
91 
92         detail = buildUidDetail(uid);
93 
94         synchronized (mUidDetailCache) {
95             mUidDetailCache.put(uid, detail);
96         }
97 
98         return detail;
99     }
100 
101     /**
102      * Build {@link UidDetail} object, blocking until all {@link Drawable}
103      * lookup is finished.
104      */
buildUidDetail(int uid)105     private UidDetail buildUidDetail(int uid) {
106         final Resources res = mContext.getResources();
107         final PackageManager pm = mContext.getPackageManager();
108 
109         final UidDetail detail = new UidDetail();
110         detail.label = pm.getNameForUid(uid);
111         detail.icon = pm.getDefaultActivityIcon();
112 
113         // handle special case labels
114         switch (uid) {
115             case Process.SYSTEM_UID:
116                 detail.label = res.getString(R.string.process_kernel_label);
117                 detail.icon = pm.getDefaultActivityIcon();
118                 return detail;
119             case TrafficStats.UID_REMOVED:
120                 detail.label = res.getString(UserManager.supportsMultipleUsers()
121                         ? R.string.data_usage_uninstalled_apps_users
122                         : R.string.data_usage_uninstalled_apps);
123                 detail.icon = pm.getDefaultActivityIcon();
124                 return detail;
125             case TrafficStats.UID_TETHERING:
126                 final TetheringManager tm = mContext.getSystemService(TetheringManager.class);
127                 detail.label = res.getString(Utils.getTetheringLabel(tm));
128                 detail.icon = pm.getDefaultActivityIcon();
129                 return detail;
130             case Process.OTA_UPDATE_UID:
131                 detail.label = res.getString(R.string.data_usage_ota);
132                 detail.icon = mContext.getDrawable(R.drawable.ic_system_update);
133                 return detail;
134         }
135 
136         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
137 
138         // Handle keys that are actually user handles
139         if (isKeyForUser(uid)) {
140             final int userHandle = getUserIdForKey(uid);
141             final UserInfo info = um.getUserInfo(userHandle);
142             if (info != null) {
143                 detail.label = Utils.getUserLabel(mContext, info);
144                 detail.icon = Utils.getUserIcon(mContext, um, info);
145                 return detail;
146             }
147         }
148 
149         // otherwise fall back to using packagemanager labels
150         final String[] packageNames = pm.getPackagesForUid(uid);
151         final int length = packageNames != null ? packageNames.length : 0;
152         try {
153             final int userId = UserHandle.getUserId(uid);
154             UserHandle userHandle = new UserHandle(userId);
155             IPackageManager ipm = AppGlobals.getPackageManager();
156             if (length == 1) {
157                 final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0],
158                         0 /* no flags */, userId);
159                 if (info != null) {
160                     detail.label = info.loadLabel(pm).toString();
161                     detail.icon = um.getBadgedIconForUser(info.loadIcon(pm),
162                             new UserHandle(userId));
163                 }
164             } else if (length > 1) {
165                 detail.detailLabels = new CharSequence[length];
166                 detail.detailContentDescriptions = new CharSequence[length];
167                 for (int i = 0; i < length; i++) {
168                     final String packageName = packageNames[i];
169                     final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
170                     final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName,
171                             0 /* no flags */, userId);
172 
173                     if (appInfo != null) {
174                         detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
175                         detail.detailContentDescriptions[i] = um.getBadgedLabelForUser(
176                                 detail.detailLabels[i], userHandle);
177                         if (packageInfo.sharedUserLabel != 0) {
178                             detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
179                                     packageInfo.applicationInfo).toString();
180                             detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle);
181                         }
182                     }
183                 }
184             }
185             detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle);
186         } catch (NameNotFoundException e) {
187             Log.w(TAG, "Error while building UI detail for uid "+uid, e);
188         } catch (RemoteException e) {
189             Log.w(TAG, "Error while building UI detail for uid "+uid, e);
190         }
191 
192         if (TextUtils.isEmpty(detail.label)) {
193             detail.label = Integer.toString(uid);
194         }
195 
196         return detail;
197     }
198 }
199