diff options
43 files changed, 1534 insertions, 915 deletions
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index c97247656540..f44d4c1ac9df 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -485,15 +485,25 @@ public final class ThreadedRenderer { } /** - * Stops any rendering into the surface. Use this if it is unclear whether + * Halts any current rendering into the surface. Use this if it is unclear whether * or not the surface used by the HardwareRenderer will be changing. It - * Suspends any rendering into the surface, but will not do any destruction + * Suspends any rendering into the surface, but will not do any destruction. + * + * Any subsequent draws will override the pause, resuming normal operation. */ boolean pauseSurface(Surface surface) { return nPauseSurface(mNativeProxy, surface); } /** + * Hard stops or resumes rendering into the surface. This flag is used to + * determine whether or not it is safe to use the given surface *at all* + */ + void setStopped(boolean stopped) { + nSetStopped(mNativeProxy, stopped); + } + + /** * Destroys all hardware rendering resources associated with the specified * view hierarchy. * @@ -988,6 +998,7 @@ public final class ThreadedRenderer { private static native void nInitialize(long nativeProxy, Surface window); private static native void nUpdateSurface(long nativeProxy, Surface window); private static native boolean nPauseSurface(long nativeProxy, Surface window); + private static native void nSetStopped(long nativeProxy, boolean stopped); private static native void nSetup(long nativeProxy, int width, int height, float lightRadius, int ambientShadowAlpha, int spotShadowAlpha); private static native void nSetLightCenter(long nativeProxy, diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5b2877faef02..5626f01d2e7b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1079,13 +1079,16 @@ public final class ViewRootImpl implements ViewParent, void setWindowStopped(boolean stopped) { if (mStopped != stopped) { mStopped = stopped; + final ThreadedRenderer renderer = mAttachInfo.mHardwareRenderer; + if (renderer != null) { + if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped); + renderer.setStopped(mStopped); + } if (!mStopped) { scheduleTraversals(); } else { - if (mAttachInfo.mHardwareRenderer != null) { - if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle()); - mAttachInfo.mHardwareRenderer.updateSurface(null); - mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView); + if (renderer != null) { + renderer.destroyHardwareResources(mView); } } } diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java index f5e09e2be71e..c0aeb595b616 100644 --- a/core/java/android/webkit/WebViewProviderResponse.java +++ b/core/java/android/webkit/WebViewProviderResponse.java @@ -21,7 +21,7 @@ import android.os.Parcel; import android.os.Parcelable; /** @hide */ -public class WebViewProviderResponse implements Parcelable { +public final class WebViewProviderResponse implements Parcelable { public WebViewProviderResponse(PackageInfo packageInfo, int status) { this.packageInfo = packageInfo; @@ -56,6 +56,6 @@ public class WebViewProviderResponse implements Parcelable { out.writeInt(status); } - PackageInfo packageInfo; - int status; + public final PackageInfo packageInfo; + public final int status; } diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 6dc251c87e5e..1c2d13d98acc 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -53,36 +53,14 @@ static void FontFamily_unref(JNIEnv* env, jobject clazz, jlong familyPtr) { fontFamily->Unref(); } -static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) { - MinikinFont* minikinFont = new MinikinFontSkia(face); +static jboolean addSkTypeface(FontFamily* family, SkTypeface* face, const void* fontData, + size_t fontSize, int ttcIndex) { + MinikinFont* minikinFont = new MinikinFontSkia(face, fontData, fontSize, ttcIndex); bool result = family->addFont(minikinFont); minikinFont->Unref(); return result; } -static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path, - jint ttcIndex) { - NPE_CHECK_RETURN_ZERO(env, path); - ScopedUtfChars str(env, path); - SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex); - if (face == NULL) { - ALOGE("addFont failed to create font %s", str.c_str()); - return false; - } - FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); - return addSkTypeface(fontFamily, face); -} - -static struct { - jmethodID mGet; - jmethodID mSize; -} gListClassInfo; - -static struct { - jfieldID mTag; - jfieldID mStyleValue; -} gAxisClassInfo; - static void release_global_ref(const void* /*data*/, void* context) { JNIEnv* env = AndroidRuntime::getJNIEnv(); bool needToAttach = (env == NULL); @@ -106,6 +84,47 @@ static void release_global_ref(const void* /*data*/, void* context) { } } +static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jobject bytebuf, + jint ttcIndex) { + NPE_CHECK_RETURN_ZERO(env, bytebuf); + const void* fontPtr = env->GetDirectBufferAddress(bytebuf); + if (fontPtr == NULL) { + ALOGE("addFont failed to create font, buffer invalid"); + return false; + } + jlong fontSize = env->GetDirectBufferCapacity(bytebuf); + if (fontSize < 0) { + ALOGE("addFont failed to create font, buffer size invalid"); + return false; + } + jobject fontRef = MakeGlobalRefOrDie(env, bytebuf); + SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast<void*>(fontRef))); + std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data)); + + SkFontMgr::FontParameters params; + params.setCollectionIndex(ttcIndex); + + SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); + SkTypeface* face = fm->createFromStream(fontData.release(), params); + if (face == NULL) { + ALOGE("addFont failed to create font"); + return false; + } + FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); + return addSkTypeface(fontFamily, face, fontPtr, (size_t)fontSize, ttcIndex); +} + +static struct { + jmethodID mGet; + jmethodID mSize; +} gListClassInfo; + +static struct { + jfieldID mTag; + jfieldID mStyleValue; +} gAxisClassInfo; + static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr, jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) { NPE_CHECK_RETURN_ZERO(env, font); @@ -133,7 +152,7 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong } } - void* fontPtr = env->GetDirectBufferAddress(font); + const void* fontPtr = env->GetDirectBufferAddress(font); if (fontPtr == NULL) { ALOGE("addFont failed to create font, buffer invalid"); return false; @@ -159,7 +178,7 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong return false; } FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); - MinikinFont* minikinFont = new MinikinFontSkia(face); + MinikinFont* minikinFont = new MinikinFontSkia(face, fontPtr, (size_t)fontSize, ttcIndex); fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic)); minikinFont->Unref(); return true; @@ -191,6 +210,7 @@ static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPt return false; } + size_t bufSize = asset->getLength(); SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset)); SkMemoryStream* stream = new SkMemoryStream(data); // CreateFromStream takes ownership of stream. @@ -200,7 +220,7 @@ static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPt return false; } FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); - return addSkTypeface(fontFamily, face); + return addSkTypeface(fontFamily, face, buf, bufSize, /* ttcIndex */ 0); } /////////////////////////////////////////////////////////////////////////////// @@ -208,7 +228,7 @@ static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPt static const JNINativeMethod gFontFamilyMethods[] = { { "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create }, { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, - { "nAddFont", "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont }, + { "nAddFont", "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont }, { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z", (void*)FontFamily_addFontWeightStyle }, { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z", diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 3d6520903a7b..ef45c87277ae 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -479,6 +479,12 @@ static jboolean android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject return proxy->pauseSurface(surface); } +static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean stopped) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setStopped(stopped); +} + static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr, jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); @@ -732,6 +738,7 @@ static const JNINativeMethod gMethods[] = { { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize }, { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface }, { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface }, + { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped }, { "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup }, { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter }, { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml new file mode 100644 index 000000000000..23809d5a3310 --- /dev/null +++ b/core/res/res/drawable/ic_corp_user_badge.xml @@ -0,0 +1,24 @@ +<!-- +Copyright (C) 2016 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="36dp" + android:height="36dp" + android:viewportWidth="36.0" + android:viewportHeight="36.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M18,0C8.06,-0 0,8.06 0,18C0,27.94 8.06,36 18,36C27.94,36 36,27.94 36,18C36,8.06 27.94,0 18,0zM15.5,10.5L20.5,10.5L21.75,11.75L21.75,13L24.66,13C25.57,13 26.34,13.74 26.34,14.66L26.34,18C26.34,18.92 25.57,19.66 24.66,19.66L19.66,19.66L19.66,18.41L16.34,18.41L16.34,19.66L11.34,19.66C10.43,19.66 9.66,18.92 9.66,18L9.66,14.66C9.66,13.74 10.43,13 11.34,13L14.25,13L14.25,11.78L15.5,10.5zM15.5,11.75L15.5,13L20.5,13L20.5,11.75L15.5,11.75zM10.5,20.5L16.34,20.5L16.34,21.75L19.66,21.75L19.66,20.5L25.5,20.5L25.5,23.84C25.5,24.76 24.76,25.5 23.84,25.5L12.16,25.5C11.24,25.5 10.5,24.76 10.5,23.84L10.5,20.5z"/> +</vector> diff --git a/core/res/res/values-w320dp/dimens.xml b/core/res/res/values-w320dp/dimens.xml new file mode 100644 index 000000000000..ad6d2ec63ce2 --- /dev/null +++ b/core/res/res/values-w320dp/dimens.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 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. +--> +<resources> + <!-- The platform's desired fixed width for a dialog along the major axis + (the screen is in landscape). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_width_major">320dp</item> + <!-- The platform's desired fixed width for a dialog along the minor axis + (the screen is in portrait). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_width_minor">320dp</item> +</resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a21f276873e8..9aec1bb5e163 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -112,10 +112,10 @@ <!-- The platform's desired fixed width for a dialog along the major axis (the screen is in landscape). This may be either a fraction or a dimension.--> - <item type="dimen" name="dialog_fixed_width_major">320dp</item> + <item type="dimen" name="dialog_fixed_width_major">100%</item> <!-- The platform's desired fixed width for a dialog along the minor axis (the screen is in portrait). This may be either a fraction or a dimension.--> - <item type="dimen" name="dialog_fixed_width_minor">320dp</item> + <item type="dimen" name="dialog_fixed_width_minor">100%</item> <!-- The platform's desired fixed height for a dialog along the major axis (the screen is in portrait). This may be either a fraction or a dimension.--> <item type="dimen" name="dialog_fixed_height_major">80%</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d0414c36c494..592904e47b5a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1265,6 +1265,7 @@ <java-symbol type="drawable" name="ic_corp_badge" /> <java-symbol type="drawable" name="ic_corp_badge_off" /> <java-symbol type="drawable" name="ic_corp_icon_badge" /> + <java-symbol type="drawable" name="ic_corp_user_badge" /> <java-symbol type="drawable" name="ic_corp_badge_no_background" /> <java-symbol type="drawable" name="ic_corp_icon" /> <java-symbol type="drawable" name="ic_corp_statusbar_icon" /> diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk index be2e6bfeb176..702218c56a0e 100644 --- a/core/tests/notificationtests/Android.mk +++ b/core/tests/notificationtests/Android.mk @@ -11,6 +11,9 @@ LOCAL_SRC_FILES := \ LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := NotificationStressTests +LOCAL_STATIC_JAVA_LIBRARIES := \ + ub-uiautomator + include $(BUILD_PACKAGE) include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/tests/notificationtests/src/android/app/NotificationStressTest.java b/core/tests/notificationtests/src/android/app/NotificationStressTest.java index 4cb617e44a38..6e86c37f65f8 100644 --- a/core/tests/notificationtests/src/android/app/NotificationStressTest.java +++ b/core/tests/notificationtests/src/android/app/NotificationStressTest.java @@ -16,15 +16,19 @@ package android.app; - import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; +import android.os.RemoteException; import android.os.SystemClock; +import android.support.test.uiautomator.UiDevice; import android.test.InstrumentationTestCase; import android.test.RepetitiveTest; -import android.test.TimedTest; +import android.util.Log; +import java.lang.InterruptedException; +import java.lang.reflect.Method; import java.util.Random; /** @@ -34,51 +38,78 @@ import java.util.Random; public class NotificationStressTest extends InstrumentationTestCase { private static final int NUM_ITERATIONS = 200; + private static final int NUM_ITERATIONS_2 = 30; + private static final int LONG_TIMEOUT = 2000; + // 50 notifications per app: defined as Variable MAX_PACKAGE_NOTIFICATIONS in + // NotificationManagerService.java + private static final int MAX_NOTIFCATIONS = 50; private static final int[] ICONS = new int[] { - android.R.drawable.stat_notify_call_mute, - android.R.drawable.stat_notify_chat, - android.R.drawable.stat_notify_error, - android.R.drawable.stat_notify_missed_call, - android.R.drawable.stat_notify_more, - android.R.drawable.stat_notify_sdcard, - android.R.drawable.stat_notify_sdcard_prepare, - android.R.drawable.stat_notify_sdcard_usb, - android.R.drawable.stat_notify_sync, - android.R.drawable.stat_notify_sync_noanim, - android.R.drawable.stat_notify_voicemail, + android.R.drawable.stat_notify_call_mute, + android.R.drawable.stat_notify_chat, + android.R.drawable.stat_notify_error, + android.R.drawable.stat_notify_missed_call, + android.R.drawable.stat_notify_more, + android.R.drawable.stat_notify_sdcard, + android.R.drawable.stat_notify_sdcard_prepare, + android.R.drawable.stat_notify_sdcard_usb, + android.R.drawable.stat_notify_sync, + android.R.drawable.stat_notify_sync_noanim, + android.R.drawable.stat_notify_voicemail, }; private final Random mRandom = new Random(); private Context mContext; private NotificationManager mNotificationManager; - private int notifyId = 0; + private UiDevice mDevice = null; + private int mNotifyId = 0; @Override protected void setUp() throws Exception { super.setUp(); + mDevice = UiDevice.getInstance(getInstrumentation()); mContext = getInstrumentation().getContext(); mNotificationManager = (NotificationManager) mContext.getSystemService( Context.NOTIFICATION_SERVICE); + mDevice.setOrientationNatural(); + mNotificationManager.cancelAll(); } @Override protected void tearDown() throws Exception { super.tearDown(); + mDevice.unfreezeRotation(); mNotificationManager.cancelAll(); } - @RepetitiveTest(numIterations=NUM_ITERATIONS) + @RepetitiveTest(numIterations = NUM_ITERATIONS) public void testNotificationStress() { // Cancel one of every five notifications to vary load on notification manager - if (notifyId % 5 == 4) { - mNotificationManager.cancel(notifyId - 4); + if (mNotifyId % 5 == 4) { + mNotificationManager.cancel(mNotifyId - 4); + } + sendNotification(mNotifyId++, "testNotificationStressNotify"); + } + + @RepetitiveTest(numIterations = NUM_ITERATIONS_2) + public void testNotificationsWithShadeStress() throws Exception { + mDevice.openNotification(); + Thread.sleep(LONG_TIMEOUT); + for (int j = 0; j < MAX_NOTIFCATIONS; j++) { + sendNotification(mNotifyId++, "testNotificationStressNotify"); + } + Thread.sleep(500); + assertTrue(mNotificationManager.getActiveNotifications().length == MAX_NOTIFCATIONS); + for (int j = 0; j < MAX_NOTIFCATIONS; j++) { + mNotificationManager.cancel(--mNotifyId); + } + if (isLockScreen()) { + fail("Notification stress test failed, back to lockscreen"); } - sendNotification(notifyId++, "testNotificationStressNotify"); } private void sendNotification(int id, CharSequence text) { // Fill in arbitrary content - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")); + Intent intent = new Intent(Intent.ACTION_VIEW); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0); CharSequence title = text + " " + id; CharSequence subtitle = String.valueOf(System.currentTimeMillis()); @@ -90,8 +121,19 @@ public class NotificationStressTest extends InstrumentationTestCase { .setContentTitle(title) .setContentText(subtitle) .setContentIntent(pendingIntent) + .setPriority(Notification.PRIORITY_HIGH) .build(); mNotificationManager.notify(id, notification); SystemClock.sleep(10); } + + private boolean isLockScreen() { + KeyguardManager myKM = (KeyguardManager) mContext + .getSystemService(Context.KEYGUARD_SERVICE); + if (myKM.inKeyguardRestrictedInputMode()) { + return true; + } else { + return false; + } + } } diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index f741e3cb5f8d..e48bf7956df3 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -17,8 +17,12 @@ package android.graphics; import android.content.res.AssetManager; +import android.util.Log; +import java.io.FileInputStream; +import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.List; /** @@ -27,6 +31,9 @@ import java.util.List; * @hide */ public class FontFamily { + + private static String TAG = "FontFamily"; + /** * @hide */ @@ -62,7 +69,15 @@ public class FontFamily { } public boolean addFont(String path, int ttcIndex) { - return nAddFont(mNativePtr, path, ttcIndex); + try (FileInputStream file = new FileInputStream(path)) { + FileChannel fileChannel = file.getChannel(); + long fontSize = fileChannel.size(); + ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); + return nAddFont(mNativePtr, fontBuffer, ttcIndex); + } catch (IOException e) { + Log.e(TAG, "Error mapping font file " + path); + return false; + } } public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes, @@ -76,7 +91,7 @@ public class FontFamily { private static native long nCreateFamily(String lang, int variant); private static native void nUnrefFamily(long nativePtr); - private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex); + private static native boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex); private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font, int ttcIndex, List<FontListParser.Axis> listOfAxis, int weight, boolean isItalic); diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index b9e33589885e..b39de266846c 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -22,8 +22,9 @@ namespace android { -MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) : - mTypeface(typeface) { +MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize, + int ttcIndex) : + mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) { } MinikinFontSkia::~MinikinFontSkia() { @@ -66,22 +67,38 @@ void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id, bounds->mBottom = skBounds.fBottom; } -bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) { - if (buf == NULL) { - const size_t tableSize = mTypeface->getTableSize(tag); - *size = tableSize; - return tableSize != 0; - } else { - const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf); - *size = actualSize; - return actualSize != 0; +const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) { + // we don't have a buffer to the font data, copy to own buffer + const size_t tableSize = mTypeface->getTableSize(tag); + *size = tableSize; + if (tableSize == 0) { + return nullptr; } + void* buf = malloc(tableSize); + if (buf == nullptr) { + return nullptr; + } + mTypeface->getTableData(tag, 0, tableSize, buf); + *destroy = free; + return buf; } SkTypeface *MinikinFontSkia::GetSkTypeface() const { return mTypeface; } +const void* MinikinFontSkia::GetFontData() const { + return mFontData; +} + +size_t MinikinFontSkia::GetFontSize() const { + return mFontSize; +} + +int MinikinFontSkia::GetFontIndex() const { + return mTtcIndex; +} + int32_t MinikinFontSkia::GetUniqueId() const { return mTypeface->uniqueID(); } diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h index 1d50168adac0..dbc65f98fe12 100644 --- a/libs/hwui/hwui/MinikinSkia.h +++ b/libs/hwui/hwui/MinikinSkia.h @@ -28,7 +28,8 @@ namespace android { class ANDROID_API MinikinFontSkia : public MinikinFont { public: // Note: this takes ownership of the reference (will unref on dtor) - explicit MinikinFontSkia(SkTypeface *typeface); + explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize, + int ttcIndex); ~MinikinFontSkia(); @@ -38,20 +39,30 @@ public: void GetBounds(MinikinRect* bounds, uint32_t glyph_id, const MinikinPaint &paint) const; - // If buf is NULL, just update size - bool GetTable(uint32_t tag, uint8_t *buf, size_t *size); + const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy); int32_t GetUniqueId() const; SkTypeface* GetSkTypeface() const; + // Access to underlying raw font bytes + const void* GetFontData() const; + size_t GetFontSize() const; + int GetFontIndex() const; + static uint32_t packPaintFlags(const SkPaint* paint); static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags); // set typeface and fake bold/italic parameters static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery); private: - SkTypeface *mTypeface; + SkTypeface* mTypeface; + + // A raw pointer to the font data - it should be owned by some other object with + // lifetime at least as long as this object. + const void* mFontData; + size_t mFontSize; + int mTtcIndex; }; } // namespace android diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index fa8ad5d6ce32..c583988554c7 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -66,7 +66,10 @@ static FontCollection *makeFontCollection() { ALOGD("makeFontCollection adding %s", fn); SkTypeface *skFace = SkTypeface::CreateFromFile(fn); if (skFace != NULL) { - MinikinFont *font = new MinikinFontSkia(skFace); + // TODO: might be a nice optimization to get access to the underlying font + // data, but would require us opening the file ourselves and passing that + // to the appropriate Create method of SkTypeface. + MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0); family->addFont(font); font->Unref(); } else { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index ab66b2aceb4c..890d4a1ea224 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -113,18 +113,11 @@ void CanvasContext::setSurface(Surface* surface) { mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); mHaveNewSurface = true; mSwapHistory.clear(); - makeCurrent(); } else { mRenderThread.removeFrameCallback(this); } } -void CanvasContext::requireSurface() { - LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, - "requireSurface() called but no surface set!"); - makeCurrent(); -} - void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { mSwapBehavior = swapBehavior; } @@ -146,6 +139,18 @@ bool CanvasContext::pauseSurface(Surface* surface) { return mRenderThread.removeFrameCallback(this); } +void CanvasContext::setStopped(bool stopped) { + if (mStopped != stopped) { + mStopped = stopped; + if (mStopped) { + mRenderThread.removeFrameCallback(this); + if (mEglManager.isCurrent(mEglSurface)) { + mEglManager.makeCurrent(EGL_NO_SURFACE); + } + } + } +} + // TODO: don't pass viewport size, it's automatic via EGL void CanvasContext::setup(int width, int height, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { @@ -172,7 +177,9 @@ void CanvasContext::setOpaque(bool opaque) { mOpaque = opaque; } -void CanvasContext::makeCurrent() { +bool CanvasContext::makeCurrent() { + if (mStopped) return false; + // TODO: Figure out why this workaround is needed, see b/13913604 // In the meantime this matches the behavior of GLRenderer, so it is not a regression EGLint error = 0; @@ -180,6 +187,7 @@ void CanvasContext::makeCurrent() { if (error) { setSurface(nullptr); } + return !error; } static bool wasSkipped(FrameInfo* info) { @@ -671,7 +679,7 @@ void CanvasContext::runWithGlContext(RenderTask* task) { } Layer* CanvasContext::createTextureLayer() { - requireSurface(); + mEglManager.initialize(); return LayerRenderer::createTextureLayer(mRenderThread.renderState()); } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 9350114e1abc..52df3abe2cae 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -82,13 +82,14 @@ public: void initialize(Surface* surface); void updateSurface(Surface* surface); bool pauseSurface(Surface* surface); + void setStopped(bool stopped); bool hasSurface() { return mNativeSurface.get(); } void setup(int width, int height, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setLightCenter(const Vector3& lightCenter); void setOpaque(bool opaque); - void makeCurrent(); + bool makeCurrent(); void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target); void draw(); @@ -172,7 +173,6 @@ private: friend class android::uirenderer::RenderState; void setSurface(Surface* window); - void requireSurface(); void freePrefetchedLayers(TreeObserver* observer); @@ -185,6 +185,7 @@ private: EglManager& mEglManager; sp<Surface> mNativeSurface; EGLSurface mEglSurface = EGL_NO_SURFACE; + bool mStopped = false; bool mBufferPreserved = false; SwapBehavior mSwapBehavior = kSwap_default; struct SwapHistory { diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 651aaa23e341..ed472ac4bd02 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -115,7 +115,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { ATRACE_CALL(); int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)]; mRenderThread->timeLord().vsyncReceived(vsync); - mContext->makeCurrent(); + bool canDraw = mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(mContext); for (size_t i = 0; i < mLayers.size(); i++) { @@ -126,8 +126,9 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { // This is after the prepareTree so that any pending operations // (RenderNode tree state, prefetched layers, etc...) will be flushed. - if (CC_UNLIKELY(!mContext->hasSurface())) { + if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) { mSyncResult |= kSync_LostSurfaceRewardIfFound; + info.out.canDrawThisFrame = false; } if (info.out.hasAnimations) { diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 8def7ad03d34..ac6a28fe6289 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -270,12 +270,6 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) { // Ensure we always have a valid surface & context surface = mPBufferSurface; } - // TODO: Temporary to help diagnose b/27286867 - if (mCurrentSurface == mPBufferSurface || surface == mPBufferSurface) { - ALOGD("Switching from surface %p%s to %p%s", mCurrentSurface, - mCurrentSurface == mPBufferSurface ? " (pbuffer)" : "", - surface, surface == mPBufferSurface ? " (pbuffer)" : ""); - } if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { if (errOut) { *errOut = eglGetError(); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 2e99d0bcb50d..c5a2dc7bd8ce 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -52,14 +52,6 @@ namespace renderthread { MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() -namespace DumpFlags { - enum { - FrameStats = 1 << 0, - Reset = 1 << 1, - JankStats = 1 << 2, - }; -}; - CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { return new CanvasContext(*args->thread, args->translucent, @@ -175,6 +167,18 @@ bool RenderProxy::pauseSurface(const sp<Surface>& surface) { return (bool) postAndWait(task); } +CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) { + args->context->setStopped(args->stopped); + return nullptr; +} + +void RenderProxy::setStopped(bool stopped) { + SETUP_TASK(setStopped); + args->context = mContext; + args->stopped = stopped; + postAndWait(task); +} + CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { args->context->setup(args->width, args->height, args->lightRadius, @@ -425,6 +429,9 @@ CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread, if (args->dumpFlags & DumpFlags::Reset) { args->context->resetFrameStats(); } + if (args->dumpFlags & DumpFlags::JankStats) { + args->thread->jankTracker().dump(args->fd); + } return nullptr; } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 97194fe6c6a6..32d3283bd814 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -50,6 +50,14 @@ class ErrorChannel; class RenderThread; class RenderProxyBridge; +namespace DumpFlags { + enum { + FrameStats = 1 << 0, + Reset = 1 << 1, + JankStats = 1 << 2, + }; +}; + /* * RenderProxy is strictly single threaded. All methods must be invoked on the owning * thread. It is important to note that RenderProxy may be deleted while it has @@ -71,6 +79,7 @@ public: ANDROID_API void initialize(const sp<Surface>& surface); ANDROID_API void updateSurface(const sp<Surface>& surface); ANDROID_API bool pauseSurface(const sp<Surface>& surface); + ANDROID_API void setStopped(bool stopped); ANDROID_API void setup(int width, int height, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setLightCenter(const Vector3& lightCenter); diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index cc0fdd5537d2..c5af06160b62 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -122,5 +122,5 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { } } - proxy->dumpProfileInfo(STDOUT_FILENO, 0); + proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); } diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 586f2690ba1f..ca0b86a52e4f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -15,7 +15,7 @@ import android.net.ConnectivityManager; import android.os.BatteryManager; import android.os.UserManager; import com.android.internal.util.UserIcons; -import com.android.settingslib.drawable.CircleFramedDrawable; +import com.android.settingslib.drawable.UserIconDrawable; import java.text.NumberFormat; @@ -73,21 +73,22 @@ public class Utils { /** * Returns a circular icon for a user. */ - public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) { + public static UserIconDrawable getUserIcon(Context context, UserManager um, UserInfo user) { + final int iconSize = UserIconDrawable.getSizeForList(context); 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); + return new UserIconDrawable(iconSize).setIcon(b).bake(); } if (user.iconPath != null) { Bitmap icon = um.getUserIcon(user.id); if (icon != null) { - return CircleFramedDrawable.getInstance(context, icon); + return new UserIconDrawable(iconSize).setIcon(icon).bake(); } } - return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap( - UserIcons.getDefaultUserIcon(user.id, /* light= */ false))); + return new UserIconDrawable(iconSize).setIconDrawable( + UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake(); } /** Formats the ratio of amount/total as a percentage. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java new file mode 100644 index 000000000000..32478a7582fb --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2016 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.annotation.NonNull; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; + +import com.android.settingslib.R; + +/** + * Converts the user avatar icon to a circularly clipped one with an optional badge and frame + */ +public class UserIconDrawable extends Drawable implements Drawable.Callback { + + private Drawable mUserDrawable; + private Bitmap mUserIcon; + private Bitmap mBitmap; // baked representation. Required for transparent border around badge + private final Paint mIconPaint = new Paint(); + private final Paint mPaint = new Paint(); + private final Matrix mIconMatrix = new Matrix(); + private float mIntrinsicRadius; + private float mDisplayRadius; + private float mPadding = 0; + private int mSize = 0; // custom "intrinsic" size for this drawable if non-zero + private boolean mInvalidated = true; + private ColorStateList mTintColor = null; + private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP; + + private float mFrameWidth; + private float mFramePadding; + private ColorStateList mFrameColor = null; + private Paint mFramePaint; + + private Drawable mBadge; + private Paint mClearPaint; + private float mBadgeRadius; + private float mBadgeMargin; + + /** + * Gets the system default managed-user badge as a drawable + * @param context + * @return drawable containing just the badge + */ + public static Drawable getManagedUserBadgeDrawable(Context context) { + int displayDensity = context.getResources().getDisplayMetrics().densityDpi; + return context.getResources().getDrawableForDensity( + com.android.internal.R.drawable.ic_corp_user_badge, + displayDensity, context.getTheme()); + } + + /** + * Gets the preferred list-item size of this drawable. + * @param context + * @return size in pixels + */ + public static int getSizeForList(Context context) { + return (int) context.getResources().getDimension(R.dimen.circle_avatar_size); + } + + public UserIconDrawable() { + this(0); + } + + /** + * Use this constructor if the drawable is intended to be placed in listviews + * @param intrinsicSize if 0, the intrinsic size will come from the icon itself + */ + public UserIconDrawable(int intrinsicSize) { + super(); + mIconPaint.setAntiAlias(true); + mIconPaint.setFilterBitmap(true); + mPaint.setFilterBitmap(true); + mPaint.setAntiAlias(true); + if (intrinsicSize > 0) { + setBounds(0, 0, intrinsicSize, intrinsicSize); + setIntrinsicSize(intrinsicSize); + } + setIcon(null); + } + + public UserIconDrawable setIcon(Bitmap icon) { + if (mUserDrawable != null) { + mUserDrawable.setCallback(null); + mUserDrawable = null; + } + mUserIcon = icon; + if (mUserIcon == null) { + mIconPaint.setShader(null); + mBitmap = null; + } else { + mIconPaint.setShader(new BitmapShader(icon, Shader.TileMode.CLAMP, + Shader.TileMode.CLAMP)); + } + onBoundsChange(getBounds()); + return this; + } + + public UserIconDrawable setIconDrawable(Drawable icon) { + if (mUserDrawable != null) { + mUserDrawable.setCallback(null); + } + mUserIcon = null; + mUserDrawable = icon; + if (mUserDrawable == null) { + mBitmap = null; + } else { + mUserDrawable.setCallback(this); + } + onBoundsChange(getBounds()); + return this; + } + + public UserIconDrawable setBadge(Drawable badge) { + mBadge = badge; + if (mBadge != null) { + if (mClearPaint == null) { + mClearPaint = new Paint(); + mClearPaint.setAntiAlias(true); + mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + mClearPaint.setStyle(Paint.Style.FILL); + } + // update metrics + onBoundsChange(getBounds()); + } else { + invalidateSelf(); + } + return this; + } + + public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) { + Drawable badge = null; + boolean isManaged = context.getSystemService(DevicePolicyManager.class) + .getProfileOwnerAsUser(userId) != null; + if (isManaged) { + badge = getManagedUserBadgeDrawable(context); + } + return setBadge(badge); + } + + public void setBadgeRadius(float radius) { + mBadgeRadius = radius; + onBoundsChange(getBounds()); + } + + public void setBadgeMargin(float margin) { + mBadgeMargin = margin; + onBoundsChange(getBounds()); + } + + /** + * Sets global padding of icon/frame. Doesn't effect the badge. + * @param padding + */ + public void setPadding(float padding) { + mPadding = padding; + onBoundsChange(getBounds()); + } + + private void initFramePaint() { + if (mFramePaint == null) { + mFramePaint = new Paint(); + mFramePaint.setStyle(Paint.Style.STROKE); + mFramePaint.setAntiAlias(true); + } + } + + public void setFrameWidth(float width) { + initFramePaint(); + mFrameWidth = width; + mFramePaint.setStrokeWidth(width); + onBoundsChange(getBounds()); + } + + public void setFramePadding(float padding) { + initFramePaint(); + mFramePadding = padding; + onBoundsChange(getBounds()); + } + + public void setFrameColor(int color) { + initFramePaint(); + mFramePaint.setColor(color); + invalidateSelf(); + } + + public void setFrameColor(ColorStateList colorList) { + initFramePaint(); + mFrameColor = colorList; + invalidateSelf(); + } + + /** + * This sets the "intrinsic" size of this drawable. Useful for views which use the drawable's + * intrinsic size for layout. It is independent of the bounds. + * @param size if 0, the intrinsic size will be set to the displayed icon's size + */ + public void setIntrinsicSize(int size) { + mSize = size; + } + + @Override + public void draw(Canvas canvas) { + if (mInvalidated) { + rebake(); + } + if (mBitmap != null) { + if (mTintColor == null) { + mPaint.setColorFilter(null); + } else { + int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor()); + if (mPaint.getColorFilter() == null) { + mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode)); + } else { + ((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode); + ((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color); + } + } + + canvas.drawBitmap(mBitmap, 0, 0, mPaint); + } + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + super.invalidateSelf(); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + } + + @Override + public void setTintList(ColorStateList tintList) { + mTintColor = tintList; + super.invalidateSelf(); + } + + @Override + public void setTintMode(@NonNull PorterDuff.Mode mode) { + mTintMode = mode; + super.invalidateSelf(); + } + + /** + * This 'bakes' the current state of this icon into a bitmap and removes/recycles the source + * bitmap/drawable. Use this when no more changes will be made and an intrinsic size is set. + * This effectively turns this into a static drawable. + */ + public UserIconDrawable bake() { + if (mSize <= 0) { + throw new IllegalStateException("Baking requires an explicit intrinsic size"); + } + onBoundsChange(new Rect(0, 0, mSize, mSize)); + rebake(); + mFrameColor = null; + mFramePaint = null; + mClearPaint = null; + if (mUserDrawable != null) { + mUserDrawable.setCallback(null); + mUserDrawable = null; + } else if (mUserIcon != null) { + mUserIcon.recycle(); + mUserIcon = null; + } + return this; + } + + private void rebake() { + mInvalidated = false; + + if (mBitmap == null || (mUserDrawable == null && mUserIcon == null)) { + return; + } + + final Canvas canvas = new Canvas(mBitmap); + canvas.drawColor(0, PorterDuff.Mode.CLEAR); + + if(mUserDrawable != null) { + mUserDrawable.draw(canvas); + } else if (mUserIcon != null) { + int saveId = canvas.save(); + canvas.concat(mIconMatrix); + canvas.drawCircle(mUserIcon.getWidth() * 0.5f, mUserIcon.getHeight() * 0.5f, + mIntrinsicRadius, mIconPaint); + canvas.restoreToCount(saveId); + } + + if (mFrameColor != null) { + mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT)); + } + if ((mFrameWidth + mFramePadding) > 0.001f) { + float radius = mDisplayRadius - mPadding - mFrameWidth * 0.5f; + canvas.drawCircle(getBounds().exactCenterX(), getBounds().exactCenterY(), + radius, mFramePaint); + } + + if ((mBadge != null) && (mBadgeRadius > 0.001f)) { + final float badgeDiameter = mBadgeRadius * 2f; + final float badgeTop = mBitmap.getHeight() - badgeDiameter; + float badgeLeft = mBitmap.getWidth() - badgeDiameter; + + mBadge.setBounds((int) badgeLeft, (int) badgeTop, + (int) (badgeLeft + badgeDiameter), (int) (badgeTop + badgeDiameter)); + + final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin; + canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius, + borderRadius, mClearPaint); + + mBadge.draw(canvas); + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + if (bounds.isEmpty() || (mUserIcon == null && mUserDrawable == null)) { + return; + } + + // re-create bitmap if applicable + float newDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f; + int size = (int) (newDisplayRadius * 2); + if (mBitmap == null || size != ((int) (mDisplayRadius * 2))) { + mDisplayRadius = newDisplayRadius; + if (mBitmap != null) { + mBitmap.recycle(); + } + mBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + } + + // update metrics + mDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f; + final float iconRadius = mDisplayRadius - mFrameWidth - mFramePadding - mPadding; + RectF dstRect = new RectF(bounds.exactCenterX() - iconRadius, + bounds.exactCenterY() - iconRadius, + bounds.exactCenterX() + iconRadius, + bounds.exactCenterY() + iconRadius); + if (mUserDrawable != null) { + Rect rounded = new Rect(); + dstRect.round(rounded); + mIntrinsicRadius = Math.min(mUserDrawable.getIntrinsicWidth(), + mUserDrawable.getIntrinsicHeight()) * 0.5f; + mUserDrawable.setBounds(rounded); + } else if (mUserIcon != null) { + // Build square-to-square transformation matrix + final float iconCX = mUserIcon.getWidth() * 0.5f; + final float iconCY = mUserIcon.getHeight() * 0.5f; + mIntrinsicRadius = Math.min(iconCX, iconCY); + RectF srcRect = new RectF(iconCX - mIntrinsicRadius, iconCY - mIntrinsicRadius, + iconCX + mIntrinsicRadius, iconCY + mIntrinsicRadius); + mIconMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL); + } + + invalidateSelf(); + } + + @Override + public void invalidateSelf() { + super.invalidateSelf(); + mInvalidated = true; + } + + @Override + public boolean isStateful() { + return mFrameColor != null && mFrameColor.isStateful(); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public int getIntrinsicWidth() { + return (mSize <= 0 ? (int) mIntrinsicRadius * 2 : mSize); + } + + @Override + public int getIntrinsicHeight() { + return getIntrinsicWidth(); + } + + @Override + public void invalidateDrawable(@NonNull Drawable who) { + invalidateSelf(); + } + + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + scheduleSelf(what, when); + } + + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + unscheduleSelf(what); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java index f9fa805f808d..750601d82ce5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java @@ -33,7 +33,7 @@ import android.widget.ListAdapter; import android.widget.SpinnerAdapter; import android.widget.TextView; import com.android.internal.util.UserIcons; -import com.android.settingslib.drawable.CircleFramedDrawable; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.settingslib.R; @@ -71,7 +71,8 @@ public class UserAdapter implements SpinnerAdapter, ListAdapter { } private static Drawable encircle(Context context, Drawable icon) { - return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon)); + return new UserIconDrawable(UserIconDrawable.getSizeForList(context)) + .setIconDrawable(icon).bake(); } } private ArrayList<UserDetails> data; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 61c9f2b15c82..5621642954ee 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1942,7 +1942,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 126; + private static final int SETTINGS_VERSION = 127; private final int mUserId; @@ -2167,6 +2167,36 @@ public class SettingsProvider extends ContentProvider { currentVersion = 126; } + if (currentVersion == 126) { + // Version 126: copy the primary values of LOCK_SCREEN_SHOW_NOTIFICATIONS and + // LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS into managed profile. + if (mUserManager.isManagedProfile(userId)) { + final SettingsState systemSecureSettings = + getSecureSettingsLocked(UserHandle.USER_SYSTEM); + + final Setting showNotifications = systemSecureSettings.getSettingLocked( + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS); + if (showNotifications != null) { + final SettingsState secureSettings = getSecureSettingsLocked(userId); + secureSettings.insertSettingLocked( + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + showNotifications.getValue(), + SettingsState.SYSTEM_PACKAGE_NAME); + } + + final Setting allowPrivate = systemSecureSettings.getSettingLocked( + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + if (allowPrivate != null) { + final SettingsState secureSettings = getSecureSettingsLocked(userId); + secureSettings.insertSettingLocked( + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + allowPrivate.getValue(), + SettingsState.SYSTEM_PACKAGE_NAME); + } + } + currentVersion = 127; + } + // vXXX: Add new settings above this point. // Return the current version. diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml new file mode 100644 index 000000000000..20251c2fedc0 --- /dev/null +++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2016 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 + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_activated="true" android:color="@color/current_user_border_color" /> + <item android:color="@android:color/transparent" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml new file mode 100644 index 000000000000..2b75c369c2e0 --- /dev/null +++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2016 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 + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" /> + <item android:color="@android:color/transparent" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml index c6e453a5d991..d6855286142b 100644 --- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml +++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml @@ -33,14 +33,18 @@ <TextView android:id="@+id/user_name" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="16dp" + android:layout_marginEnd="13dp" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" /> <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture" - android:layout_width="@dimen/max_avatar_size" - android:layout_height="@dimen/max_avatar_size" + android:layout_width="@dimen/framed_avatar_size" + android:layout_height="@dimen/framed_avatar_size" android:contentDescription="@null" + android:backgroundTint="@color/qs_user_detail_avatar_tint" + android:backgroundTintMode="src_atop" sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness" - sysui:framePadding="6dp" - sysui:activeFrameColor="@color/current_user_border_color" /> + sysui:framePadding="2.5dp" + sysui:badgeDiameter="18dp" + sysui:badgeMargin="1dp" + sysui:frameColor="@color/qs_user_detail_avatar_frame" /> </com.android.systemui.qs.tiles.UserDetailItemView> diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml index 661d74a468e4..58fc0694c72c 100644 --- a/packages/SystemUI/res/layout/qs_user_detail_item.xml +++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml @@ -33,12 +33,16 @@ <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture" - android:layout_width="@dimen/max_avatar_size" - android:layout_height="@dimen/max_avatar_size" - android:layout_marginBottom="10dp" + android:layout_width="@dimen/framed_avatar_size" + android:layout_height="@dimen/framed_avatar_size" + android:layout_marginBottom="7dp" + android:backgroundTint="@color/qs_user_detail_avatar_tint" + android:backgroundTintMode="src_atop" systemui:frameWidth="2dp" - systemui:framePadding="6dp" - systemui:activeFrameColor="@color/current_user_border_color"/> + systemui:framePadding="2.5dp" + systemui:badgeDiameter="18dp" + systemui:badgeMargin="1dp" + systemui:frameColor="@color/qs_user_detail_avatar_frame"/> <LinearLayout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 6dd8c5260b89..1543360761bf 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -53,10 +53,14 @@ <enum name="vertical" value="1" /> </attr> <declare-styleable name="UserAvatarView"> + <attr name="avatarPadding" format="dimension" /> <attr name="frameWidth" format="dimension" /> <attr name="framePadding" format="dimension" /> + <!-- {@deprecated Use a statelist in frameColor instead.} --> <attr name="activeFrameColor" format="color" /> - <attr name="frameColor" /> + <attr name="frameColor" format="color" /> + <attr name="badgeDiameter" format="dimension" /> + <attr name="badgeMargin" format="dimension" /> </declare-styleable> <declare-styleable name="UserDetailItemView"> <attr name="regularFontFamily" format="string" /> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a4eadbfb5601..cf2e3384de08 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -388,6 +388,9 @@ quick settings header --> <dimen name="max_avatar_size">48dp</dimen> + <!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) --> + <dimen name="framed_avatar_size">54dp</dimen> + <!-- Margin on the left side of the carrier text on Keyguard --> <dimen name="keyguard_carrier_text_margin">16dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java deleted file mode 100644 index 1933bbce6e3e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2014 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.systemui; - -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.RectF; -import android.graphics.Shader; - -public class BitmapHelper { - /** - * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled - * to fit and clipped to an inscribed circle. - * @param input Bitmap to resize and clip - * @param width Width of output bitmap (and diameter of circle) - * @param height Height of output bitmap - * @return A shiny new bitmap for you to use - */ - public static Bitmap createCircularClip(Bitmap input, int width, int height) { - if (input == null) return null; - - final int inWidth = input.getWidth(); - final int inHeight = input.getHeight(); - final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(output); - final Paint paint = new Paint(); - paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); - paint.setAntiAlias(true); - final RectF srcRect = new RectF(0, 0, inWidth, inHeight); - final RectF dstRect = new RectF(0, 0, width, height); - final Matrix m = new Matrix(); - m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER); - canvas.setMatrix(m); - canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint); - return output; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java index fcf758b45868..99eae024a7c7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java @@ -30,6 +30,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.util.ArrayUtils; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.statusbar.phone.UserAvatarView; @@ -87,25 +88,29 @@ public class UserDetailItemView extends LinearLayout { return (UserDetailItemView) convertView; } - public void bind(String name, Bitmap picture) { + public void bind(String name, Bitmap picture, int userId) { mName.setText(name); - mAvatar.setBitmap(picture); + mAvatar.setAvatarWithBadge(picture, userId); } - public void bind(String name, Drawable picture) { + public void bind(String name, Drawable picture, int userId) { mName.setText(name); - mAvatar.setDrawable(picture); + mAvatar.setDrawableWithBadge(picture, userId); + } + + public void setAvatarEnabled(boolean enabled) { + mAvatar.setEnabled(enabled); } public void setDisabledByAdmin(boolean disabled) { mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE); mName.setEnabled(!disabled); - mAvatar.setDisabled(disabled); + mAvatar.setEnabled(!disabled); } public void setEnabled(boolean enabled) { mName.setEnabled(enabled); - mAvatar.setDisabled(!enabled); + mAvatar.setEnabled(enabled); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index da9876235263..d4fa765cbefb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -76,9 +76,9 @@ public class UserDetailView extends PseudoGridView { } String name = getName(mContext, item); if (item.picture == null) { - v.bind(name, getDrawable(mContext, item)); + v.bind(name, getDrawable(mContext, item), item.resolveId()); } else { - v.bind(name, item.picture); + v.bind(name, item.picture, item.info.id); } v.setActivated(item.isCurrent); v.setDisabledByAdmin(item.isDisabledByAdmin); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java index 093a827272b8..dc1b35d6c701 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java @@ -17,69 +17,57 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.R; /** - * A view that displays a user image cropped to a circle with a frame. + * A view that displays a user image cropped to a circle with an optional frame. */ public class UserAvatarView extends View { - private int mActiveFrameColor; - private int mFrameColor; - private float mFrameWidth; - private float mFramePadding; - private Bitmap mBitmap; - private Drawable mDrawable; - private boolean mIsDisabled; - - private final Paint mFramePaint = new Paint(); - private final Paint mBitmapPaint = new Paint(); - private final Matrix mDrawMatrix = new Matrix(); - - private float mScale = 1; + private final UserIconDrawable mDrawable = new UserIconDrawable(); public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes); final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { + case R.styleable.UserAvatarView_avatarPadding: + setAvatarPadding(a.getDimension(attr, 0)); + break; case R.styleable.UserAvatarView_frameWidth: setFrameWidth(a.getDimension(attr, 0)); break; case R.styleable.UserAvatarView_framePadding: setFramePadding(a.getDimension(attr, 0)); break; - case R.styleable.UserAvatarView_activeFrameColor: - setActiveFrameColor(a.getColor(attr, 0)); - break; case R.styleable.UserAvatarView_frameColor: - setFrameColor(a.getColor(attr, 0)); + setFrameColor(a.getColorStateList(attr)); + break; + case R.styleable.UserAvatarView_badgeDiameter: + setBadgeDiameter(a.getDimension(attr, 0)); + break; + case R.styleable.UserAvatarView_badgeMargin: + setBadgeMargin(a.getDimension(attr, 0)); break; } } a.recycle(); - - mFramePaint.setAntiAlias(true); - mFramePaint.setStyle(Paint.Style.STROKE); - mBitmapPaint.setAntiAlias(true); + setBackground(mDrawable); } public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) { @@ -94,180 +82,61 @@ public class UserAvatarView extends View { this(context, null); } + /** + * @deprecated use {@link #setAvatar(Bitmap)} instead. + */ + @Deprecated public void setBitmap(Bitmap bitmap) { - setDrawable(null); - mBitmap = bitmap; - if (mBitmap != null) { - mBitmapPaint.setShader(new BitmapShader( - bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); - } else { - mBitmapPaint.setShader(null); - } - configureBounds(); - invalidate(); + setAvatar(bitmap); } - public void setFrameColor(int frameColor) { - mFrameColor = frameColor; - invalidate(); - } - - public void setActiveFrameColor(int activeFrameColor) { - mActiveFrameColor = activeFrameColor; - invalidate(); + public void setFrameColor(ColorStateList color) { + mDrawable.setFrameColor(color); } public void setFrameWidth(float frameWidth) { - mFrameWidth = frameWidth; - invalidate(); + mDrawable.setFrameWidth(frameWidth); } public void setFramePadding(float framePadding) { - mFramePadding = framePadding; - invalidate(); + mDrawable.setFramePadding(framePadding); } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - configureBounds(); + public void setAvatarPadding(float avatarPadding) { + mDrawable.setPadding(avatarPadding); } - public void configureBounds() { - int vwidth = getWidth() - mPaddingLeft - mPaddingRight; - int vheight = getHeight() - mPaddingTop - mPaddingBottom; - - int dwidth; - int dheight; - if (mBitmap != null) { - dwidth = mBitmap.getWidth(); - dheight = mBitmap.getHeight(); - } else if (mDrawable != null) { - vwidth -= 2 * (mFrameWidth - 1); - vheight -= 2 * (mFrameWidth - 1); - dwidth = vwidth; - dheight = vheight; - mDrawable.setBounds(0, 0, dwidth, dheight); - } else { - return; - } - - float scale; - float dx; - float dy; - - scale = Math.min((float) vwidth / (float) dwidth, - (float) vheight / (float) dheight); - - dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f); - dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); - - mDrawMatrix.setScale(scale, scale); - mDrawMatrix.postTranslate(dx, dy); - mScale = scale; + public void setBadgeDiameter(float diameter) { + mDrawable.setBadgeRadius(diameter * 0.5f); } - @Override - protected void onDraw(Canvas canvas) { - int frameColor = isActivated() ? mActiveFrameColor : mFrameColor; - float halfW = getWidth() / 2f; - float halfH = getHeight() / 2f; - float halfSW = Math.min(halfH, halfW); - updateDrawableIfDisabled(); - if (mBitmap != null && mScale > 0) { - int saveCount = canvas.getSaveCount(); - canvas.save(); - canvas.translate(mPaddingLeft, mPaddingTop); - canvas.concat(mDrawMatrix); - float halfBW = mBitmap.getWidth() / 2f; - float halfBH = mBitmap.getHeight() / 2f; - float halfBSW = Math.min(halfBH, halfBW); - canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint); - canvas.restoreToCount(saveCount); - } else if (mDrawable != null && mScale > 0) { - int saveCount = canvas.getSaveCount(); - canvas.save(); - canvas.translate(mPaddingLeft, mPaddingTop); - canvas.translate(mFrameWidth - 1, mFrameWidth - 1); - canvas.concat(mDrawMatrix); - mDrawable.draw(canvas); - canvas.restoreToCount(saveCount); - } - if (frameColor != 0) { - mFramePaint.setColor(frameColor); - mFramePaint.setStrokeWidth(mFrameWidth); - canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f, - mFramePaint); - } - } - - public void setDrawable(Drawable d) { - if (mDrawable != null) { - mDrawable.setCallback(null); - unscheduleDrawable(mDrawable); - } - mDrawable = d; - if (d != null) { - d.setCallback(this); - if (d.isStateful()) { - d.setState(getDrawableState()); - } - d.setLayoutDirection(getLayoutDirection()); - configureBounds(); - } - if (d != null) { - mBitmap = null; - } - configureBounds(); - invalidate(); - } - - @Override - public void invalidateDrawable(Drawable dr) { - if (dr == mDrawable) { - invalidate(); - } else { - super.invalidateDrawable(dr); - } + public void setBadgeMargin(float margin) { + mDrawable.setBadgeMargin(margin); } - @Override - protected boolean verifyDrawable(Drawable who) { - return who == mDrawable || super.verifyDrawable(who); + public void setAvatar(Bitmap avatar) { + mDrawable.setIcon(avatar); + mDrawable.setBadge(null); } - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - if (mDrawable != null && mDrawable.isStateful()) { - mDrawable.setState(getDrawableState()); - } + public void setAvatarWithBadge(Bitmap avatar, int userId) { + mDrawable.setIcon(avatar); + mDrawable.setBadgeIfManagedUser(getContext(), userId); } - public void setDisabled(boolean disabled) { - if (mIsDisabled == disabled) { - return; + public void setDrawable(Drawable d) { + if (d instanceof UserIconDrawable) { + throw new RuntimeException("Recursively adding UserIconDrawable"); } - mIsDisabled = disabled; - invalidate(); + mDrawable.setIconDrawable(d); + mDrawable.setBadge(null); } - private void updateDrawableIfDisabled() { - int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color); - PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor, - PorterDuff.Mode.SRC_ATOP); - if (mBitmap != null) { - if (mIsDisabled) { - mBitmapPaint.setColorFilter(filter); - } else { - mBitmapPaint.setColorFilter(null); - } - } else if (mDrawable != null) { - if (mIsDisabled) { - mDrawable.setColorFilter(filter); - } else { - mDrawable.setColorFilter(null); - } + public void setDrawableWithBadge(Drawable d, int userId) { + if (d instanceof UserIconDrawable) { + throw new RuntimeException("Recursively adding UserIconDrawable"); } + mDrawable.setIconDrawable(d); + mDrawable.setBadgeIfManagedUser(getContext(), userId); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index fb310a64a358..c39d718a44f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -253,17 +253,13 @@ public class KeyguardUserSwitcher { UserDetailItemView v = (UserDetailItemView) convertView; String name = getName(mContext, item); - Drawable drawable; if (item.picture == null) { - drawable = getDrawable(mContext, item).mutate(); + v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId()); } else { - drawable = new BitmapDrawable(mContext.getResources(), item.picture); + v.bind(name, item.picture, item.info.id); } // Disable the icon if switching is disabled - if (!item.isSwitchToEnabled) { - drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color)); - } - v.bind(name, drawable); + v.setAvatarEnabled(item.isSwitchToEnabled); convertView.setActivated(item.isCurrent); convertView.setTag(item); return convertView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java index 85ac755e6cc3..bae5bdab07bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java @@ -26,7 +26,6 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.RemoteException; @@ -37,7 +36,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.util.UserIcons; -import com.android.systemui.BitmapHelper; +import com.android.settingslib.drawable.UserIconDrawable; import com.android.systemui.R; import java.util.ArrayList; @@ -155,8 +154,8 @@ public final class UserInfoController { Drawable avatar = null; Bitmap rawAvatar = um.getUserIcon(userId); if (rawAvatar != null) { - avatar = new BitmapDrawable(mContext.getResources(), - BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize)); + avatar = new UserIconDrawable(avatarSize) + .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake(); } else { avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId, /* light= */ true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index ea0bdf29c25c..c82ba3b16890 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -50,7 +50,6 @@ import android.widget.BaseAdapter; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.util.UserIcons; import com.android.settingslib.RestrictedLockUtils; -import com.android.systemui.BitmapHelper; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; @@ -197,8 +196,6 @@ public class UserSwitcherController { boolean canSwitchUsers = mUserManager.canSwitchUsers(); UserInfo currentUserInfo = null; UserRecord guestRecord = null; - int avatarSize = mContext.getResources() - .getDimensionPixelSize(R.dimen.max_avatar_size); for (UserInfo info : infos) { boolean isCurrent = currentId == info.id; @@ -219,8 +216,10 @@ public class UserSwitcherController { picture = mUserManager.getUserIcon(info.id); if (picture != null) { - picture = BitmapHelper.createCircularClip( - picture, avatarSize, avatarSize); + int avatarSize = mContext.getResources() + .getDimensionPixelSize(R.dimen.max_avatar_size); + picture = Bitmap.createScaledBitmap( + picture, avatarSize, avatarSize, true); } } int index = isCurrent ? 0 : records.size(); @@ -664,8 +663,7 @@ public class UserSwitcherController { if (item.isAddUser) { return context.getDrawable(R.drawable.ic_add_circle_qs); } - return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id, - /* light= */ true); + return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true); } public void refresh() { @@ -718,6 +716,13 @@ public class UserSwitcherController { isSwitchToEnabled); } + public int resolveId() { + if (isGuest || info == null) { + return UserHandle.USER_NULL; + } + return info.id; + } + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("UserRecord("); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index f6d2f7e06ed3..83681853898c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -905,7 +905,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); synchronized (mSessions) { - mSessions.get(sessionId).setPermissionsResult(accepted); + PackageInstallerSession session = mSessions.get(sessionId); + if (session != null) { + session.setPermissionsResult(accepted); + } } } diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index ebec44554210..1f6fb2aad43a 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -20,17 +20,12 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.Signature; import android.os.Binder; import android.os.PatternMatcher; import android.os.Process; import android.os.ResultReceiver; import android.os.UserHandle; -import android.util.Base64; import android.util.Slog; import android.webkit.IWebViewUpdateService; import android.webkit.WebViewFactory; @@ -40,9 +35,7 @@ import android.webkit.WebViewProviderResponse; import com.android.server.SystemService; import java.io.FileDescriptor; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * Private service to wait for the updatable WebView to be ready for use. @@ -53,19 +46,16 @@ public class WebViewUpdateService extends SystemService { private static final String TAG = "WebViewUpdateService"; private BroadcastReceiver mWebViewUpdatedReceiver; - private SystemInterface mSystemInterface; + private WebViewUpdateServiceImpl mImpl; static final int PACKAGE_CHANGED = 0; static final int PACKAGE_ADDED = 1; static final int PACKAGE_ADDED_REPLACED = 2; static final int PACKAGE_REMOVED = 3; - private WebViewUpdater mWebViewUpdater; - public WebViewUpdateService(Context context) { super(context); - mSystemInterface = new SystemImpl(); - mWebViewUpdater = new WebViewUpdater(getContext(), mSystemInterface); + mImpl = new WebViewUpdateServiceImpl(context, new SystemImpl()); } @Override @@ -82,24 +72,26 @@ public class WebViewUpdateService extends SystemService { // the package that is being replaced we early-out here so that we don't // run the update-logic twice. if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return; - packageStateChanged(packageNameFromIntent(intent), PACKAGE_REMOVED); + mImpl.packageStateChanged(packageNameFromIntent(intent), + PACKAGE_REMOVED); break; case Intent.ACTION_PACKAGE_CHANGED: // Ensure that we only heed PACKAGE_CHANGED intents if they change an // entire package, not just a component if (entirePackageChanged(intent)) { - packageStateChanged(packageNameFromIntent(intent), PACKAGE_CHANGED); + mImpl.packageStateChanged(packageNameFromIntent(intent), + PACKAGE_CHANGED); } break; case Intent.ACTION_PACKAGE_ADDED: - packageStateChanged(packageNameFromIntent(intent), + mImpl.packageStateChanged(packageNameFromIntent(intent), (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING) ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED)); break; case Intent.ACTION_USER_ADDED: int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - handleNewUser(userId); + mImpl.handleNewUser(userId); break; } } @@ -110,7 +102,7 @@ public class WebViewUpdateService extends SystemService { filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); // Make sure we only receive intents for WebView packages from our config file. - for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + for (WebViewProviderInfo provider : mImpl.getWebViewPackages()) { filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL); } getContext().registerReceiver(mWebViewUpdatedReceiver, filter); @@ -122,518 +114,14 @@ public class WebViewUpdateService extends SystemService { publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/); } - private void packageStateChanged(String packageName, int changedState) { - updateFallbackState(packageName, changedState); - mWebViewUpdater.packageStateChanged(packageName, changedState); - } - public void prepareWebViewInSystemServer() { - updateFallbackStateOnBoot(); - mWebViewUpdater.prepareWebViewInSystemServer(); + mImpl.prepareWebViewInSystemServer(); } private static String packageNameFromIntent(Intent intent) { return intent.getDataString().substring("package:".length()); } - private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) { - for (WebViewProviderInfo provider : providers) { - if (provider.availableByDefault && !provider.isFallback) { - try { - PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider); - if (isEnabledPackage(packageInfo) - && mWebViewUpdater.isValidProvider(provider, packageInfo)) { - return true; - } - } catch (NameNotFoundException e) { - // A non-existent provider is neither valid nor enabled - } - } - } - return false; - } - - /** - * Called when a new user has been added to update the state of its fallback package. - */ - void handleNewUser(int userId) { - if (!mSystemInterface.isFallbackLogicEnabled()) return; - - WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); - WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); - if (fallbackProvider == null) return; - - mSystemInterface.enablePackageForUser(fallbackProvider.packageName, - !existsValidNonFallbackProvider(webviewProviders), userId); - } - - public void updateFallbackStateOnBoot() { - WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); - updateFallbackState(webviewProviders, true); - } - - /** - * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback - * package that is valid (and available by default) then disable the fallback package, - * otherwise, enable the fallback package. - */ - public void updateFallbackState(String changedPackage, int changedState) { - if (!mSystemInterface.isFallbackLogicEnabled()) return; - - WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); - - // A package was changed / updated / downgraded, early out if it is not one of the - // webview packages that are available by default. - boolean changedPackageAvailableByDefault = false; - for (WebViewProviderInfo provider : webviewProviders) { - if (provider.packageName.equals(changedPackage)) { - if (provider.availableByDefault) { - changedPackageAvailableByDefault = true; - } - break; - } - } - if (!changedPackageAvailableByDefault) return; - updateFallbackState(webviewProviders, false); - } - - private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) { - // If there exists a valid and enabled non-fallback package - disable the fallback - // package, otherwise, enable it. - WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); - if (fallbackProvider == null) return; - boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders); - - boolean isFallbackEnabled = false; - try { - isFallbackEnabled = isEnabledPackage( - mSystemInterface.getPackageInfoForProvider(fallbackProvider)); - } catch (NameNotFoundException e) { - } - - if (existsValidNonFallbackProvider - // During an OTA the primary user's WebView state might differ from other users', so - // ignore the state of that user during boot. - && (isFallbackEnabled || isBoot)) { - mSystemInterface.uninstallAndDisablePackageForAllUsers(getContext(), - fallbackProvider.packageName); - } else if (!existsValidNonFallbackProvider - // During an OTA the primary user's WebView state might differ from other users', so - // ignore the state of that user during boot. - && (!isFallbackEnabled || isBoot)) { - // Enable the fallback package for all users. - mSystemInterface.enablePackageForAllUsers(getContext(), - fallbackProvider.packageName, true); - } - } - - /** - * Returns the only fallback provider in the set of given packages, or null if there is none. - */ - private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { - for (WebViewProviderInfo provider : webviewPackages) { - if (provider.isFallback) { - return provider; - } - } - return null; - } - - private boolean isFallbackPackage(String packageName) { - if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false; - - WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages(); - WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages); - return (fallbackProvider != null - && packageName.equals(fallbackProvider.packageName)); - } - - /** - * Class that decides what WebView implementation to use and prepares that implementation for - * use. - */ - private static class WebViewUpdater { - private Context mContext; - private SystemInterface mSystemInterface; - private int mMinimumVersionCode = -1; - - public WebViewUpdater(Context context, SystemInterface systemInterface) { - mContext = context; - mSystemInterface = systemInterface; - } - - private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000. - - // Keeps track of the number of running relro creations - private int mNumRelroCreationsStarted = 0; - private int mNumRelroCreationsFinished = 0; - // Implies that we need to rerun relro creation because we are using an out-of-date package - private boolean mWebViewPackageDirty = false; - private boolean mAnyWebViewInstalled = false; - - private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; - - // The WebView package currently in use (or the one we are preparing). - private PackageInfo mCurrentWebViewPackage = null; - - private Object mLock = new Object(); - - public void packageStateChanged(String packageName, int changedState) { - for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { - String webviewPackage = provider.packageName; - - if (webviewPackage.equals(packageName)) { - boolean updateWebView = false; - boolean removedOrChangedOldPackage = false; - String oldProviderName = null; - PackageInfo newPackage = null; - synchronized(mLock) { - try { - newPackage = findPreferredWebViewPackage(); - if (mCurrentWebViewPackage != null) - oldProviderName = mCurrentWebViewPackage.packageName; - // Only trigger update actions if the updated package is the one - // that will be used, or the one that was in use before the - // update, or if we haven't seen a valid WebView package before. - updateWebView = - provider.packageName.equals(newPackage.packageName) - || provider.packageName.equals(oldProviderName) - || mCurrentWebViewPackage == null; - // We removed the old package if we received an intent to remove - // or replace the old package. - removedOrChangedOldPackage = - provider.packageName.equals(oldProviderName); - if (updateWebView) { - onWebViewProviderChanged(newPackage); - } - } catch (WebViewFactory.MissingWebViewPackageException e) { - Slog.e(TAG, "Could not find valid WebView package to create " + - "relro with " + e); - } - } - if(updateWebView && !removedOrChangedOldPackage - && oldProviderName != null) { - // If the provider change is the result of adding or replacing a - // package that was not the previous provider then we must kill - // packages dependent on the old package ourselves. The framework - // only kills dependents of packages that are being removed. - mSystemInterface.killPackageDependents(oldProviderName); - } - return; - } - } - } - - public void prepareWebViewInSystemServer() { - try { - synchronized(mLock) { - mCurrentWebViewPackage = findPreferredWebViewPackage(); - onWebViewProviderChanged(mCurrentWebViewPackage); - } - } catch (Throwable t) { - // Log and discard errors at this stage as we must not crash the system server. - Slog.e(TAG, "error preparing webview provider from system server", t); - } - } - - /** - * Change WebView provider and provider setting and kill packages using the old provider. - * Return the new provider (in case we are in the middle of creating relro files this new - * provider will not be in use directly, but will when the relros are done). - */ - public String changeProviderAndSetting(String newProviderName) { - PackageInfo oldPackage = null; - PackageInfo newPackage = null; - synchronized(mLock) { - oldPackage = mCurrentWebViewPackage; - mSystemInterface.updateUserSetting(mContext, newProviderName); - - try { - newPackage = findPreferredWebViewPackage(); - if (oldPackage != null - && newPackage.packageName.equals(oldPackage.packageName)) { - // If we don't perform the user change, revert the settings change. - mSystemInterface.updateUserSetting(mContext, newPackage.packageName); - return newPackage.packageName; - } - } catch (WebViewFactory.MissingWebViewPackageException e) { - Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " + - "package " + e); - // If we don't perform the user change but don't have an installed WebView - // package, we will have changed the setting and it will be used when a package - // is available. - return newProviderName; - } - onWebViewProviderChanged(newPackage); - } - // Kill apps using the old provider - if (oldPackage != null) { - mSystemInterface.killPackageDependents(oldPackage.packageName); - } - return newPackage.packageName; - } - - /** - * This is called when we change WebView provider, either when the current provider is - * updated or a new provider is chosen / takes precedence. - */ - private void onWebViewProviderChanged(PackageInfo newPackage) { - synchronized(mLock) { - mAnyWebViewInstalled = true; - if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { - mCurrentWebViewPackage = newPackage; - mSystemInterface.updateUserSetting(mContext, newPackage.packageName); - - // The relro creations might 'finish' (not start at all) before - // WebViewFactory.onWebViewProviderChanged which means we might not know the - // number of started creations before they finish. - mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; - mNumRelroCreationsFinished = 0; - mNumRelroCreationsStarted = - mSystemInterface.onWebViewProviderChanged(newPackage); - // If the relro creations finish before we know the number of started creations - // we will have to do any cleanup/notifying here. - checkIfRelrosDoneLocked(); - } else { - mWebViewPackageDirty = true; - } - } - } - - private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() { - WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); - List<ProviderAndPackageInfo> providers = new ArrayList<>(); - for(int n = 0; n < allProviders.length; n++) { - try { - PackageInfo packageInfo = - mSystemInterface.getPackageInfoForProvider(allProviders[n]); - if (isValidProvider(allProviders[n], packageInfo)) { - providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); - } - } catch (NameNotFoundException e) { - // Don't add non-existent packages - } - } - return providers.toArray(new ProviderAndPackageInfo[providers.size()]); - } - - /** - * Fetch only the currently valid WebView packages. - **/ - public WebViewProviderInfo[] getValidWebViewPackages() { - ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); - WebViewProviderInfo[] providers = - new WebViewProviderInfo[providersAndPackageInfos.length]; - for(int n = 0; n < providersAndPackageInfos.length; n++) { - providers[n] = providersAndPackageInfos[n].provider; - } - return providers; - } - - - private class ProviderAndPackageInfo { - public final WebViewProviderInfo provider; - public final PackageInfo packageInfo; - - public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { - this.provider = provider; - this.packageInfo = packageInfo; - } - } - - /** - * Returns either the package info of the WebView provider determined in the following way: - * If the user has chosen a provider then use that if it is valid, - * otherwise use the first package in the webview priority list that is valid. - * - */ - private PackageInfo findPreferredWebViewPackage() { - ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); - - String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext); - - // If the user has chosen provider, use that - for (ProviderAndPackageInfo providerAndPackage : providers) { - if (providerAndPackage.provider.packageName.equals(userChosenProvider) - && isEnabledPackage(providerAndPackage.packageInfo)) { - return providerAndPackage.packageInfo; - } - } - - // User did not choose, or the choice failed; use the most stable provider that is - // enabled and available by default (not through user choice). - for (ProviderAndPackageInfo providerAndPackage : providers) { - if (providerAndPackage.provider.availableByDefault - && isEnabledPackage(providerAndPackage.packageInfo)) { - return providerAndPackage.packageInfo; - } - } - - // Could not find any enabled package either, use the most stable provider. - for (ProviderAndPackageInfo providerAndPackage : providers) { - return providerAndPackage.packageInfo; - } - - mAnyWebViewInstalled = false; - throw new WebViewFactory.MissingWebViewPackageException( - "Could not find a loadable WebView package"); - } - - public void notifyRelroCreationCompleted() { - synchronized (mLock) { - mNumRelroCreationsFinished++; - checkIfRelrosDoneLocked(); - } - } - - public WebViewProviderResponse waitForAndGetProvider() { - PackageInfo webViewPackage = null; - final long NS_PER_MS = 1000000; - final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; - boolean webViewReady = false; - int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; - synchronized (mLock) { - webViewReady = webViewIsReadyLocked(); - while (!webViewReady) { - final long timeNowMs = System.nanoTime() / NS_PER_MS; - if (timeNowMs >= timeoutTimeMs) break; - try { - mLock.wait(timeoutTimeMs - timeNowMs); - } catch (InterruptedException e) {} - webViewReady = webViewIsReadyLocked(); - } - // Make sure we return the provider that was used to create the relro file - webViewPackage = mCurrentWebViewPackage; - if (webViewReady) { - } else if (!mAnyWebViewInstalled) { - webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; - } else { - // Either the current relro creation isn't done yet, or the new relro creatioin - // hasn't kicked off yet (the last relro creation used an out-of-date WebView). - webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; - } - } - if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); - return new WebViewProviderResponse(webViewPackage, webViewStatus); - } - - public String getCurrentWebViewPackageName() { - synchronized(mLock) { - if (mCurrentWebViewPackage == null) - return null; - return mCurrentWebViewPackage.packageName; - } - } - - /** - * Returns whether WebView is ready and is not going to go through its preparation phase - * again directly. - */ - private boolean webViewIsReadyLocked() { - return !mWebViewPackageDirty - && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) - // The current package might be replaced though we haven't received an intent - // declaring this yet, the following flag makes anyone loading WebView to wait in - // this case. - && mAnyWebViewInstalled; - } - - private void checkIfRelrosDoneLocked() { - if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { - if (mWebViewPackageDirty) { - mWebViewPackageDirty = false; - // If we have changed provider since we started the relro creation we need to - // redo the whole process using the new package instead. - PackageInfo newPackage = findPreferredWebViewPackage(); - onWebViewProviderChanged(newPackage); - } else { - mLock.notifyAll(); - } - } - } - - /** - * Returns whether this provider is valid for use as a WebView provider. - */ - public boolean isValidProvider(WebViewProviderInfo configInfo, - PackageInfo packageInfo) { - if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 - && packageInfo.versionCode < getMinimumVersionCode() - && !mSystemInterface.systemIsDebuggable()) { - // Non-system package webview providers may be downgraded arbitrarily low, prevent - // that by enforcing minimum version code. This check is only enforced for user - // builds. - return false; - } - if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) && - WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) { - return true; - } - return false; - } - - /** - * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode - * of all available-by-default and non-fallback WebView provider packages. If there is no - * such WebView provider package on the system, then return -1, which means all positive - * versionCode WebView packages are accepted. - */ - private int getMinimumVersionCode() { - if (mMinimumVersionCode > 0) { - return mMinimumVersionCode; - } - - for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { - if (provider.availableByDefault && !provider.isFallback) { - try { - int versionCode = - mSystemInterface.getFactoryPackageVersion(provider.packageName); - if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) { - mMinimumVersionCode = versionCode; - } - } catch (PackageManager.NameNotFoundException e) { - // Safe to ignore. - } - } - } - - return mMinimumVersionCode; - } - } - - private static boolean providerHasValidSignature(WebViewProviderInfo provider, - PackageInfo packageInfo, SystemInterface systemInterface) { - if (systemInterface.systemIsDebuggable()) { - return true; - } - Signature[] packageSignatures; - // If no signature is declared, instead check whether the package is included in the - // system. - if (provider.signatures == null || provider.signatures.length == 0) { - return packageInfo.applicationInfo.isSystemApp(); - } - packageSignatures = packageInfo.signatures; - if (packageSignatures.length != 1) - return false; - - final byte[] packageSignature = packageSignatures[0].toByteArray(); - // Return whether the package signature matches any of the valid signatures - for (String signature : provider.signatures) { - final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT); - if (Arrays.equals(packageSignature, validSignature)) - return true; - } - return false; - } - - /** - * Returns whether the given package is enabled. - * This state can be changed by the user from Settings->Apps - */ - private static boolean isEnabledPackage(PackageInfo packageInfo) { - return packageInfo.applicationInfo.enabled; - } - /** * Returns whether the entire package from an ACTION_PACKAGE_CHANGED intent was changed (rather * than just one of its components). @@ -673,7 +161,7 @@ public class WebViewUpdateService extends SystemService { long callingId = Binder.clearCallingIdentity(); try { - WebViewUpdateService.this.mWebViewUpdater.notifyRelroCreationCompleted(); + WebViewUpdateService.this.mImpl.notifyRelroCreationCompleted(); } finally { Binder.restoreCallingIdentity(callingId); } @@ -693,7 +181,7 @@ public class WebViewUpdateService extends SystemService { throw new IllegalStateException("Cannot create a WebView from the SystemServer"); } - return WebViewUpdateService.this.mWebViewUpdater.waitForAndGetProvider(); + return WebViewUpdateService.this.mImpl.waitForAndGetProvider(); } /** @@ -714,7 +202,7 @@ public class WebViewUpdateService extends SystemService { long callingId = Binder.clearCallingIdentity(); try { - return WebViewUpdateService.this.mWebViewUpdater.changeProviderAndSetting( + return WebViewUpdateService.this.mImpl.changeProviderAndSetting( newProvider); } finally { Binder.restoreCallingIdentity(callingId); @@ -723,22 +211,22 @@ public class WebViewUpdateService extends SystemService { @Override // Binder call public WebViewProviderInfo[] getValidWebViewPackages() { - return WebViewUpdateService.this.mWebViewUpdater.getValidWebViewPackages(); + return WebViewUpdateService.this.mImpl.getValidWebViewPackages(); } @Override // Binder call public WebViewProviderInfo[] getAllWebViewPackages() { - return WebViewUpdateService.this.mSystemInterface.getWebViewPackages(); + return WebViewUpdateService.this.mImpl.getWebViewPackages(); } @Override // Binder call public String getCurrentWebViewPackageName() { - return WebViewUpdateService.this.mWebViewUpdater.getCurrentWebViewPackageName(); + return WebViewUpdateService.this.mImpl.getCurrentWebViewPackageName(); } @Override // Binder call public boolean isFallbackPackage(String packageName) { - return WebViewUpdateService.this.isFallbackPackage(packageName); + return WebViewUpdateService.this.mImpl.isFallbackPackage(packageName); } @Override // Binder call @@ -754,7 +242,7 @@ public class WebViewUpdateService extends SystemService { throw new SecurityException(msg); } - WebViewUpdateService.this.mSystemInterface.enableFallbackLogic(enable); + WebViewUpdateService.this.mImpl.enableFallbackLogic(enable); } } } diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java new file mode 100644 index 000000000000..32b195b4c71b --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2016 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.server.webkit; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.util.Base64; +import android.util.Slog; +import android.webkit.WebViewFactory; +import android.webkit.WebViewProviderInfo; +import android.webkit.WebViewProviderResponse; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Implementation of the WebViewUpdateService. + * This class doesn't depend on the android system like the actual Service does and can be used + * directly by tests (as long as they implement a SystemInterface). + * @hide + */ +public class WebViewUpdateServiceImpl { + private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName(); + + private SystemInterface mSystemInterface; + private WebViewUpdater mWebViewUpdater; + private Context mContext; + + public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) { + mContext = context; + mSystemInterface = systemInterface; + mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface); + } + + void packageStateChanged(String packageName, int changedState) { + updateFallbackStateOnPackageChange(packageName, changedState); + mWebViewUpdater.packageStateChanged(packageName, changedState); + } + + void prepareWebViewInSystemServer() { + updateFallbackStateOnBoot(); + mWebViewUpdater.prepareWebViewInSystemServer(); + } + + private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) { + for (WebViewProviderInfo provider : providers) { + if (provider.availableByDefault && !provider.isFallback) { + try { + PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider); + if (isEnabledPackage(packageInfo) + && mWebViewUpdater.isValidProvider(provider, packageInfo)) { + return true; + } + } catch (NameNotFoundException e) { + // A non-existent provider is neither valid nor enabled + } + } + } + return false; + } + + /** + * Called when a new user has been added to update the state of its fallback package. + */ + void handleNewUser(int userId) { + if (!mSystemInterface.isFallbackLogicEnabled()) return; + + WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); + WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); + if (fallbackProvider == null) return; + + mSystemInterface.enablePackageForUser(fallbackProvider.packageName, + !existsValidNonFallbackProvider(webviewProviders), userId); + } + + void notifyRelroCreationCompleted() { + mWebViewUpdater.notifyRelroCreationCompleted(); + } + + WebViewProviderResponse waitForAndGetProvider() { + return mWebViewUpdater.waitForAndGetProvider(); + } + + String changeProviderAndSetting(String newProvider) { + return mWebViewUpdater.changeProviderAndSetting(newProvider); + } + + WebViewProviderInfo[] getValidWebViewPackages() { + return mWebViewUpdater.getValidWebViewPackages(); + } + + WebViewProviderInfo[] getWebViewPackages() { + return mSystemInterface.getWebViewPackages(); + } + + String getCurrentWebViewPackageName() { + return mWebViewUpdater.getCurrentWebViewPackageName(); + } + + void enableFallbackLogic(boolean enable) { + mSystemInterface.enableFallbackLogic(enable); + } + + private void updateFallbackStateOnBoot() { + WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); + updateFallbackState(webviewProviders, true); + } + + /** + * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback + * package that is valid (and available by default) then disable the fallback package, + * otherwise, enable the fallback package. + */ + private void updateFallbackStateOnPackageChange(String changedPackage, int changedState) { + if (!mSystemInterface.isFallbackLogicEnabled()) return; + + WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); + + // A package was changed / updated / downgraded, early out if it is not one of the + // webview packages that are available by default. + boolean changedPackageAvailableByDefault = false; + for (WebViewProviderInfo provider : webviewProviders) { + if (provider.packageName.equals(changedPackage)) { + if (provider.availableByDefault) { + changedPackageAvailableByDefault = true; + } + break; + } + } + if (!changedPackageAvailableByDefault) return; + updateFallbackState(webviewProviders, false); + } + + private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) { + // If there exists a valid and enabled non-fallback package - disable the fallback + // package, otherwise, enable it. + WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); + if (fallbackProvider == null) return; + boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders); + + boolean isFallbackEnabled = false; + try { + isFallbackEnabled = isEnabledPackage( + mSystemInterface.getPackageInfoForProvider(fallbackProvider)); + } catch (NameNotFoundException e) { + } + + if (existsValidNonFallbackProvider + // During an OTA the primary user's WebView state might differ from other users', so + // ignore the state of that user during boot. + && (isFallbackEnabled || isBoot)) { + mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext, + fallbackProvider.packageName); + } else if (!existsValidNonFallbackProvider + // During an OTA the primary user's WebView state might differ from other users', so + // ignore the state of that user during boot. + && (!isFallbackEnabled || isBoot)) { + // Enable the fallback package for all users. + mSystemInterface.enablePackageForAllUsers(mContext, + fallbackProvider.packageName, true); + } + } + + /** + * Returns the only fallback provider in the set of given packages, or null if there is none. + */ + private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { + for (WebViewProviderInfo provider : webviewPackages) { + if (provider.isFallback) { + return provider; + } + } + return null; + } + + boolean isFallbackPackage(String packageName) { + if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false; + + WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages(); + WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages); + return (fallbackProvider != null + && packageName.equals(fallbackProvider.packageName)); + } + + /** + * Class that decides what WebView implementation to use and prepares that implementation for + * use. + */ + private static class WebViewUpdater { + private Context mContext; + private SystemInterface mSystemInterface; + private int mMinimumVersionCode = -1; + + public WebViewUpdater(Context context, SystemInterface systemInterface) { + mContext = context; + mSystemInterface = systemInterface; + } + + private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000. + + // Keeps track of the number of running relro creations + private int mNumRelroCreationsStarted = 0; + private int mNumRelroCreationsFinished = 0; + // Implies that we need to rerun relro creation because we are using an out-of-date package + private boolean mWebViewPackageDirty = false; + private boolean mAnyWebViewInstalled = false; + + private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; + + // The WebView package currently in use (or the one we are preparing). + private PackageInfo mCurrentWebViewPackage = null; + + private Object mLock = new Object(); + + public void packageStateChanged(String packageName, int changedState) { + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + String webviewPackage = provider.packageName; + + if (webviewPackage.equals(packageName)) { + boolean updateWebView = false; + boolean removedOrChangedOldPackage = false; + String oldProviderName = null; + PackageInfo newPackage = null; + synchronized(mLock) { + try { + newPackage = findPreferredWebViewPackage(); + if (mCurrentWebViewPackage != null) + oldProviderName = mCurrentWebViewPackage.packageName; + // Only trigger update actions if the updated package is the one + // that will be used, or the one that was in use before the + // update, or if we haven't seen a valid WebView package before. + updateWebView = + provider.packageName.equals(newPackage.packageName) + || provider.packageName.equals(oldProviderName) + || mCurrentWebViewPackage == null; + // We removed the old package if we received an intent to remove + // or replace the old package. + removedOrChangedOldPackage = + provider.packageName.equals(oldProviderName); + if (updateWebView) { + onWebViewProviderChanged(newPackage); + } + } catch (WebViewFactory.MissingWebViewPackageException e) { + Slog.e(TAG, "Could not find valid WebView package to create " + + "relro with " + e); + } + } + if(updateWebView && !removedOrChangedOldPackage + && oldProviderName != null) { + // If the provider change is the result of adding or replacing a + // package that was not the previous provider then we must kill + // packages dependent on the old package ourselves. The framework + // only kills dependents of packages that are being removed. + mSystemInterface.killPackageDependents(oldProviderName); + } + return; + } + } + } + + public void prepareWebViewInSystemServer() { + try { + synchronized(mLock) { + mCurrentWebViewPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(mCurrentWebViewPackage); + } + } catch (Throwable t) { + // Log and discard errors at this stage as we must not crash the system server. + Slog.e(TAG, "error preparing webview provider from system server", t); + } + } + + /** + * Change WebView provider and provider setting and kill packages using the old provider. + * Return the new provider (in case we are in the middle of creating relro files this new + * provider will not be in use directly, but will when the relros are done). + */ + public String changeProviderAndSetting(String newProviderName) { + PackageInfo oldPackage = null; + PackageInfo newPackage = null; + synchronized(mLock) { + oldPackage = mCurrentWebViewPackage; + mSystemInterface.updateUserSetting(mContext, newProviderName); + + try { + newPackage = findPreferredWebViewPackage(); + if (oldPackage != null + && newPackage.packageName.equals(oldPackage.packageName)) { + // If we don't perform the user change, revert the settings change. + mSystemInterface.updateUserSetting(mContext, newPackage.packageName); + return newPackage.packageName; + } + } catch (WebViewFactory.MissingWebViewPackageException e) { + Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " + + "package " + e); + // If we don't perform the user change but don't have an installed WebView + // package, we will have changed the setting and it will be used when a package + // is available. + return newProviderName; + } + onWebViewProviderChanged(newPackage); + } + // Kill apps using the old provider + if (oldPackage != null) { + mSystemInterface.killPackageDependents(oldPackage.packageName); + } + return newPackage.packageName; + } + + /** + * This is called when we change WebView provider, either when the current provider is + * updated or a new provider is chosen / takes precedence. + */ + private void onWebViewProviderChanged(PackageInfo newPackage) { + synchronized(mLock) { + mAnyWebViewInstalled = true; + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + mCurrentWebViewPackage = newPackage; + mSystemInterface.updateUserSetting(mContext, newPackage.packageName); + + // The relro creations might 'finish' (not start at all) before + // WebViewFactory.onWebViewProviderChanged which means we might not know the + // number of started creations before they finish. + mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; + mNumRelroCreationsFinished = 0; + mNumRelroCreationsStarted = + mSystemInterface.onWebViewProviderChanged(newPackage); + // If the relro creations finish before we know the number of started creations + // we will have to do any cleanup/notifying here. + checkIfRelrosDoneLocked(); + } else { + mWebViewPackageDirty = true; + } + } + } + + private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() { + WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); + List<ProviderAndPackageInfo> providers = new ArrayList<>(); + for(int n = 0; n < allProviders.length; n++) { + try { + PackageInfo packageInfo = + mSystemInterface.getPackageInfoForProvider(allProviders[n]); + if (isValidProvider(allProviders[n], packageInfo)) { + providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); + } + } catch (NameNotFoundException e) { + // Don't add non-existent packages + } + } + return providers.toArray(new ProviderAndPackageInfo[providers.size()]); + } + + /** + * Fetch only the currently valid WebView packages. + **/ + public WebViewProviderInfo[] getValidWebViewPackages() { + ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); + WebViewProviderInfo[] providers = + new WebViewProviderInfo[providersAndPackageInfos.length]; + for(int n = 0; n < providersAndPackageInfos.length; n++) { + providers[n] = providersAndPackageInfos[n].provider; + } + return providers; + } + + + private class ProviderAndPackageInfo { + public final WebViewProviderInfo provider; + public final PackageInfo packageInfo; + + public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { + this.provider = provider; + this.packageInfo = packageInfo; + } + } + + /** + * Returns either the package info of the WebView provider determined in the following way: + * If the user has chosen a provider then use that if it is valid, + * otherwise use the first package in the webview priority list that is valid. + * + */ + private PackageInfo findPreferredWebViewPackage() { + ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); + + String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext); + + // If the user has chosen provider, use that + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.packageName.equals(userChosenProvider) + && isEnabledPackage(providerAndPackage.packageInfo)) { + return providerAndPackage.packageInfo; + } + } + + // User did not choose, or the choice failed; use the most stable provider that is + // enabled and available by default (not through user choice). + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.availableByDefault + && isEnabledPackage(providerAndPackage.packageInfo)) { + return providerAndPackage.packageInfo; + } + } + + // Could not find any enabled package either, use the most stable provider. + for (ProviderAndPackageInfo providerAndPackage : providers) { + return providerAndPackage.packageInfo; + } + + mAnyWebViewInstalled = false; + throw new WebViewFactory.MissingWebViewPackageException( + "Could not find a loadable WebView package"); + } + + public void notifyRelroCreationCompleted() { + synchronized (mLock) { + mNumRelroCreationsFinished++; + checkIfRelrosDoneLocked(); + } + } + + public WebViewProviderResponse waitForAndGetProvider() { + PackageInfo webViewPackage = null; + final long NS_PER_MS = 1000000; + final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; + boolean webViewReady = false; + int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; + synchronized (mLock) { + webViewReady = webViewIsReadyLocked(); + while (!webViewReady) { + final long timeNowMs = System.nanoTime() / NS_PER_MS; + if (timeNowMs >= timeoutTimeMs) break; + try { + mLock.wait(timeoutTimeMs - timeNowMs); + } catch (InterruptedException e) {} + webViewReady = webViewIsReadyLocked(); + } + // Make sure we return the provider that was used to create the relro file + webViewPackage = mCurrentWebViewPackage; + if (webViewReady) { + } else if (!mAnyWebViewInstalled) { + webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; + } else { + // Either the current relro creation isn't done yet, or the new relro creatioin + // hasn't kicked off yet (the last relro creation used an out-of-date WebView). + webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; + } + } + if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); + return new WebViewProviderResponse(webViewPackage, webViewStatus); + } + + public String getCurrentWebViewPackageName() { + synchronized(mLock) { + if (mCurrentWebViewPackage == null) + return null; + return mCurrentWebViewPackage.packageName; + } + } + + /** + * Returns whether WebView is ready and is not going to go through its preparation phase + * again directly. + */ + private boolean webViewIsReadyLocked() { + return !mWebViewPackageDirty + && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) + // The current package might be replaced though we haven't received an intent + // declaring this yet, the following flag makes anyone loading WebView to wait in + // this case. + && mAnyWebViewInstalled; + } + + private void checkIfRelrosDoneLocked() { + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + if (mWebViewPackageDirty) { + mWebViewPackageDirty = false; + // If we have changed provider since we started the relro creation we need to + // redo the whole process using the new package instead. + PackageInfo newPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(newPackage); + } else { + mLock.notifyAll(); + } + } + } + + /** + * Returns whether this provider is valid for use as a WebView provider. + */ + public boolean isValidProvider(WebViewProviderInfo configInfo, + PackageInfo packageInfo) { + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 + && packageInfo.versionCode < getMinimumVersionCode() + && !mSystemInterface.systemIsDebuggable()) { + // Non-system package webview providers may be downgraded arbitrarily low, prevent + // that by enforcing minimum version code. This check is only enforced for user + // builds. + return false; + } + if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) && + WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) { + return true; + } + return false; + } + + /** + * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode + * of all available-by-default and non-fallback WebView provider packages. If there is no + * such WebView provider package on the system, then return -1, which means all positive + * versionCode WebView packages are accepted. + */ + private int getMinimumVersionCode() { + if (mMinimumVersionCode > 0) { + return mMinimumVersionCode; + } + + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + if (provider.availableByDefault && !provider.isFallback) { + try { + int versionCode = + mSystemInterface.getFactoryPackageVersion(provider.packageName); + if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) { + mMinimumVersionCode = versionCode; + } + } catch (NameNotFoundException e) { + // Safe to ignore. + } + } + } + + return mMinimumVersionCode; + } + } + + private static boolean providerHasValidSignature(WebViewProviderInfo provider, + PackageInfo packageInfo, SystemInterface systemInterface) { + if (systemInterface.systemIsDebuggable()) { + return true; + } + Signature[] packageSignatures; + // If no signature is declared, instead check whether the package is included in the + // system. + if (provider.signatures == null || provider.signatures.length == 0) { + return packageInfo.applicationInfo.isSystemApp(); + } + packageSignatures = packageInfo.signatures; + if (packageSignatures.length != 1) + return false; + + final byte[] packageSignature = packageSignatures[0].toByteArray(); + // Return whether the package signature matches any of the valid signatures + for (String signature : provider.signatures) { + final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT); + if (Arrays.equals(packageSignature, validSignature)) + return true; + } + return false; + } + + /** + * Returns whether the given package is enabled. + * This state can be changed by the user from Settings->Apps + */ + private static boolean isEnabledPackage(PackageInfo packageInfo) { + return packageInfo.applicationInfo.enabled; + } + +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index fdea84b4c2a4..72eebb5f62f7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8078,7 +8078,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } - return null; + // We're not specifying the device admin because there isn't one. + return intent; } } |