Merge "*ring ring* Who is it? Destiny? I've been expecting your call..." into cw-e-dev
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 1c4b05f..3ad8f21 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -21,4 +21,6 @@
 
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
-</resources>
\ No newline at end of file
+
+    <dimen name="circle_avatar_size">40dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b03f100..9b1f103 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -195,4 +195,34 @@
     <!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_wifi_signal_full">Wifi signal full.</string>
 
+    <!-- Label for kernel threads in battery usage -->
+    <string name="process_kernel_label">Android OS</string>
+    <!-- Title of data usage item that represents all uninstalled applications. [CHAR LIMIT=48] -->
+    <string name="data_usage_uninstalled_apps">Removed apps</string>
+    <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] -->
+    <string name="data_usage_uninstalled_apps_users">Removed apps and users</string>
+
+    <!-- Tethering controls, item title to go into the tethering settings -->
+    <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_usb">USB tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only Wifi tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_wifi">Portable hotspot</string>
+    <!-- Tethering controls, item title to go into the tethering settings when only Bluetooth tethering is available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_bluetooth">Bluetooth tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_usb_bluetooth">Tethering</string>
+    <!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=25]-->
+    <string name="tether_settings_title_all">Tethering &amp; portable hotspot</string>
+
+    <!-- Title for a work profile. [CHAR LIMIT=25] -->
+    <string name="managed_user_title">Work profile</string>
+
+    <!-- Title for Guest user [CHAR LIMIT=35] -->
+    <string name="user_guest">Guest</string>
+
+    <!-- Manage apps, individual app screen, substituted for the application's label when the app's label CAN NOT be determined.-->
+    <string name="unknown">Unknown</string>
+
+    <!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
+    <string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
new file mode 100644
index 0000000..621a09cd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -0,0 +1,84 @@
+package com.android.settingslib;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+public final class Utils {
+
+    /**
+     * Return string resource that best describes combination of tethering
+     * options available on this device.
+     */
+    public static int getTetheringLabel(ConnectivityManager cm) {
+        String[] usbRegexs = cm.getTetherableUsbRegexs();
+        String[] wifiRegexs = cm.getTetherableWifiRegexs();
+        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+        boolean usbAvailable = usbRegexs.length != 0;
+        boolean wifiAvailable = wifiRegexs.length != 0;
+        boolean bluetoothAvailable = bluetoothRegexs.length != 0;
+
+        if (wifiAvailable && usbAvailable && bluetoothAvailable) {
+            return R.string.tether_settings_title_all;
+        } else if (wifiAvailable && usbAvailable) {
+            return R.string.tether_settings_title_all;
+        } else if (wifiAvailable && bluetoothAvailable) {
+            return R.string.tether_settings_title_all;
+        } else if (wifiAvailable) {
+            return R.string.tether_settings_title_wifi;
+        } else if (usbAvailable && bluetoothAvailable) {
+            return R.string.tether_settings_title_usb_bluetooth;
+        } else if (usbAvailable) {
+            return R.string.tether_settings_title_usb;
+        } else {
+            return R.string.tether_settings_title_bluetooth;
+        }
+    }
+
+    /**
+     * Returns a label for the user, in the form of "User: user name" or "Work profile".
+     */
+    public static String getUserLabel(Context context, UserInfo info) {
+        String name = info != null ? info.name : null;
+        if (info.isManagedProfile()) {
+            // We use predefined values for managed profiles
+            return context.getString(R.string.managed_user_title);
+        } else if (info.isGuest()) {
+            name = context.getString(R.string.user_guest);
+        }
+        if (name == null && info != null) {
+            name = Integer.toString(info.id);
+        } else if (info == null) {
+            name = context.getString(R.string.unknown);
+        }
+        return context.getResources().getString(R.string.running_process_item_user_label, name);
+    }
+
+    /**
+     * Returns a circular icon for a user.
+     */
+    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+        if (user.isManagedProfile()) {
+            // We use predefined values for managed profiles
+            Bitmap b = BitmapFactory.decodeResource(context.getResources(),
+                    com.android.internal.R.drawable.ic_corp_icon);
+            return CircleFramedDrawable.getInstance(context, b);
+        }
+        if (user.iconPath != null) {
+            Bitmap icon = um.getUserIcon(user.id);
+            if (icon != null) {
+                return CircleFramedDrawable.getInstance(context, icon);
+            }
+        }
+        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
+                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
new file mode 100644
index 0000000..278b57d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 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 com.android.settingslib.drawable;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one.
+ * TODO: Move this to an internal framework class and share with the one in Keyguard.
+ */
+public class CircleFramedDrawable extends Drawable {
+
+    private final Bitmap mBitmap;
+    private final int mSize;
+    private final Paint mPaint;
+
+    private float mScale;
+    private Rect mSrcRect;
+    private RectF mDstRect;
+
+    public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
+        Resources res = context.getResources();
+        float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+
+        CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+        return instance;
+    }
+
+    public CircleFramedDrawable(Bitmap icon, int size) {
+        super();
+        mSize = size;
+
+        mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(mBitmap);
+
+        final int width = icon.getWidth();
+        final int height = icon.getHeight();
+        final int square = Math.min(width, height);
+
+        final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+        final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+
+        final Path fillPath = new Path();
+        fillPath.addArc(circleRect, 0f, 360f);
+
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        // opaque circle matte
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setColor(Color.BLACK);
+        mPaint.setStyle(Paint.Style.FILL);
+        canvas.drawPath(fillPath, mPaint);
+
+        // mask in the icon where the bitmap is opaque
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+
+        // prepare paint for frame drawing
+        mPaint.setXfermode(null);
+
+        mScale = 1f;
+
+        mSrcRect = new Rect(0, 0, mSize, mSize);
+        mDstRect = new RectF(0, 0, mSize, mSize);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final float inside = mScale * mSize;
+        final float pad = (mSize - inside) / 2f;
+
+        mDstRect.set(pad, pad, mSize - pad, mSize - pad);
+        canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+    }
+
+    public void setScale(float scale) {
+        mScale = scale;
+    }
+
+    public float getScale() {
+        return mScale;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+    }
+    
+    @Override
+    public int getIntrinsicWidth() {
+        return mSize;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mSize;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
new file mode 100644
index 0000000..5e42281
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 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 com.android.settingslib.net;
+
+import android.graphics.drawable.Drawable;
+
+public class UidDetail {
+    public CharSequence label;
+    public CharSequence contentDescription;
+    public CharSequence[] detailLabels;
+    public CharSequence[] detailContentDescriptions;
+    public Drawable icon;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
new file mode 100644
index 0000000..224b967
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011 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 com.android.settingslib.net;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.TrafficStats;
+import android.os.UserManager;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+
+/**
+ * Return details about a specific UID, handling special cases like
+ * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
+ */
+public class UidDetailProvider {
+    private static final String TAG = "DataUsage";
+    private final Context mContext;
+    private final SparseArray<UidDetail> mUidDetailCache;
+
+    public static final int OTHER_USER_RANGE_START = -2000;
+
+    public static int buildKeyForUser(int userHandle) {
+        return OTHER_USER_RANGE_START - userHandle;
+    }
+
+    public static boolean isKeyForUser(int key) {
+        return key <= OTHER_USER_RANGE_START;
+    }
+
+    public static int getUserIdForKey(int key) {
+        return OTHER_USER_RANGE_START - key;
+    }
+
+    public UidDetailProvider(Context context) {
+        mContext = context.getApplicationContext();
+        mUidDetailCache = new SparseArray<UidDetail>();
+    }
+
+    public void clearCache() {
+        synchronized (mUidDetailCache) {
+            mUidDetailCache.clear();
+        }
+    }
+
+    /**
+     * Resolve best descriptive label for the given UID.
+     */
+    public UidDetail getUidDetail(int uid, boolean blocking) {
+        UidDetail detail;
+
+        synchronized (mUidDetailCache) {
+            detail = mUidDetailCache.get(uid);
+        }
+
+        if (detail != null) {
+            return detail;
+        } else if (!blocking) {
+            return null;
+        }
+
+        detail = buildUidDetail(uid);
+
+        synchronized (mUidDetailCache) {
+            mUidDetailCache.put(uid, detail);
+        }
+
+        return detail;
+    }
+
+    /**
+     * Build {@link UidDetail} object, blocking until all {@link Drawable}
+     * lookup is finished.
+     */
+    private UidDetail buildUidDetail(int uid) {
+        final Resources res = mContext.getResources();
+        final PackageManager pm = mContext.getPackageManager();
+
+        final UidDetail detail = new UidDetail();
+        detail.label = pm.getNameForUid(uid);
+        detail.icon = pm.getDefaultActivityIcon();
+
+        // handle special case labels
+        switch (uid) {
+            case android.os.Process.SYSTEM_UID:
+                detail.label = res.getString(R.string.process_kernel_label);
+                detail.icon = pm.getDefaultActivityIcon();
+                return detail;
+            case TrafficStats.UID_REMOVED:
+                detail.label = res.getString(UserManager.supportsMultipleUsers()
+                        ? R.string.data_usage_uninstalled_apps_users
+                        : R.string.data_usage_uninstalled_apps);
+                detail.icon = pm.getDefaultActivityIcon();
+                return detail;
+            case TrafficStats.UID_TETHERING:
+                final ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                        Context.CONNECTIVITY_SERVICE);
+                detail.label = res.getString(Utils.getTetheringLabel(cm));
+                detail.icon = pm.getDefaultActivityIcon();
+                return detail;
+        }
+
+        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+        // Handle keys that are actually user handles
+        if (isKeyForUser(uid)) {
+            final int userHandle = getUserIdForKey(uid);
+            final UserInfo info = um.getUserInfo(userHandle);
+            if (info != null) {
+                detail.label = Utils.getUserLabel(mContext, info);
+                detail.icon = Utils.getUserIcon(mContext, um, info);
+                return detail;
+            }
+        }
+
+        // otherwise fall back to using packagemanager labels
+        final String[] packageNames = pm.getPackagesForUid(uid);
+        final int length = packageNames != null ? packageNames.length : 0;
+        try {
+            final int userId = UserHandle.getUserId(uid);
+            UserHandle userHandle = new UserHandle(userId);
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            if (length == 1) {
+                final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0],
+                        0 /* no flags */, userId);
+                if (info != null) {
+                    detail.label = info.loadLabel(pm).toString();
+                    detail.icon = um.getBadgedIconForUser(info.loadIcon(pm),
+                            new UserHandle(userId));
+                }
+            } else if (length > 1) {
+                detail.detailLabels = new CharSequence[length];
+                detail.detailContentDescriptions = new CharSequence[length];
+                for (int i = 0; i < length; i++) {
+                    final String packageName = packageNames[i];
+                    final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
+                    final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName,
+                            0 /* no flags */, userId);
+
+                    if (appInfo != null) {
+                        detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
+                        detail.detailContentDescriptions[i] = um.getBadgedLabelForUser(
+                                detail.detailLabels[i], userHandle);
+                        if (packageInfo.sharedUserLabel != 0) {
+                            detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
+                                    packageInfo.applicationInfo).toString();
+                            detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle);
+                        }
+                    }
+                }
+            }
+            detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+        }
+
+        if (TextUtils.isEmpty(detail.label)) {
+            detail.label = Integer.toString(uid);
+        }
+
+        return detail;
+    }
+}