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