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