Merge "Disable long-press home Orb animation." into oc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 4961312..25e998c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -919,6 +919,7 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -1524,6 +1525,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
+ field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
field public static final int windowMinWidthMinor = 16843607; // 0x1010357
diff --git a/api/system-current.txt b/api/system-current.txt
index e867245..f5015f8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1049,6 +1049,7 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -1660,6 +1661,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
+ field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
field public static final int windowMinWidthMinor = 16843607; // 0x1010357
diff --git a/api/test-current.txt b/api/test-current.txt
index 9487740..bbaf021 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -919,6 +919,7 @@
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
field public static final int navigationBarColor = 16843858; // 0x1010452
+ field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
field public static final int navigationIcon = 16843968; // 0x10104c0
field public static final int navigationMode = 16843471; // 0x10102cf
@@ -1524,6 +1525,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
+ field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
field public static final int windowMinWidthMinor = 16843607; // 0x1010357
diff --git a/config/preloaded-classes b/config/preloaded-classes
index ff61d18..479cda4 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2192,6 +2192,7 @@
android.util.Log
android.util.Log$1
android.util.Log$ImmediateLogWriter
+android.util.Log$PreloadHolder
android.util.Log$TerribleFailureHandler
android.util.LogPrinter
android.util.LongArray
diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra
index 959fff5..09f393a 100644
--- a/config/preloaded-classes-extra
+++ b/config/preloaded-classes-extra
@@ -8,6 +8,7 @@
android.media.SoundPool
android.text.format.Formatter
android.text.Html$HtmlParser
+android.util.Log$PreloadHolder
com.android.org.conscrypt.TrustedCertificateStore
org.ccil.cowan.tagsoup.HTMLScanner
sun.security.jca.Providers
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 00d6657..1a2dc5c 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -843,7 +843,7 @@
// Assumes forward playing from here on.
for (int i = 0; i < mEvents.size(); i++) {
AnimationEvent event = mEvents.get(i);
- if (event.getTime() > currentPlayTime) {
+ if (event.getTime() > currentPlayTime || event.getTime() == DURATION_INFINITE) {
break;
}
@@ -1264,7 +1264,8 @@
} else {
for (int i = mLastEventId + 1; i < size; i++) {
AnimationEvent event = mEvents.get(i);
- if (event.getTime() <= currentPlayTime) {
+ // TODO: need a function that accounts for infinite duration to compare time
+ if (event.getTime() != DURATION_INFINITE && event.getTime() <= currentPlayTime) {
latestId = i;
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2e4ce18..f01feea 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5267,7 +5267,7 @@
final ApplicationInfo aInfo =
sPackageManager.getApplicationInfo(
packageName,
- 0 /*flags*/,
+ PackageManager.GET_SHARED_LIBRARY_FILES,
UserHandle.myUserId());
if (mActivities.size() > 0) {
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index af13e69..0d14a8d 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -176,7 +176,7 @@
Drawable d = imageView.getDrawable();
Drawable bg = imageView.getBackground();
if (d != null && (bg == null || bg.getAlpha() == 0)) {
- Bitmap bitmap = TransitionUtils.createDrawableBitmap(d);
+ Bitmap bitmap = TransitionUtils.createDrawableBitmap(d, imageView);
if (bitmap != null) {
Bundle bundle = new Bundle();
if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 55c22de..c208f1d 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1260,12 +1260,12 @@
* <p>Typically used when the view associated with the view is a container for an HTML
* document.
*
- * <strong>WARNING:</strong> a {@link android.service.autofill.AutofillService} should only
- * use this domain for autofill purposes when it trusts the app generating it (i.e., the app
- * defined by {@link AssistStructure#getActivityComponent()}).
+ * <p><b>Warning:</b> an autofill service cannot trust the value reported by this method
+ * without verifing its authenticity—see the "Web security" section of
+ * {@link android.service.autofill.AutofillService} for more details.
*
* @return domain-only part of the document. For example, if the full URL is
- * {@code http://my.site/login?user=my_user}, it returns {@code my.site}.
+ * {@code https://my.site/login?user=my_user}, it returns {@code my.site}.
*/
@Nullable public String getWebDomain() {
return mWebDomain;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 744ee8e..d7ecc81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2078,16 +2078,30 @@
* {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
* due to device configuration.
*
+ * <p>If this app does not have permission to use this API, it will always
+ * return false rather than throw an exception.</p>
+ *
+ * <p>If the device has a hotspot provisioning app, the caller is required to hold the
+ * {@link android.Manifest.permission.TETHER_PRIVILEGED} permission.</p>
+ *
+ * <p>Otherwise, this method requires the caller to hold the ability to modify system
+ * settings as determined by {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @return a boolean - {@code true} indicating Tethering is supported.
*
* {@hide}
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ @RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,
+ android.Manifest.permission.WRITE_SETTINGS})
public boolean isTetheringSupported() {
+ String pkgName = mContext.getOpPackageName();
try {
- String pkgName = mContext.getOpPackageName();
return mService.isTetheringSupported(pkgName);
+ } catch (SecurityException e) {
+ // This API is not available to this caller, but for backward-compatibility
+ // this will just return false instead of throwing.
+ return false;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/ParcelableException.java b/core/java/android/os/ParcelableException.java
index d84d629..7f71905 100644
--- a/core/java/android/os/ParcelableException.java
+++ b/core/java/android/os/ParcelableException.java
@@ -52,10 +52,12 @@
final String msg = in.readString();
try {
final Class<?> clazz = Class.forName(name, true, Parcelable.class.getClassLoader());
- return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+ if (Throwable.class.isAssignableFrom(clazz)) {
+ return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+ }
} catch (ReflectiveOperationException e) {
- throw new RuntimeException(name + ": " + msg);
}
+ return new RuntimeException(name + ": " + msg);
}
/** {@hide} */
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 3e08dcf..045c833 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -305,7 +305,7 @@
* <li>Use the {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()} to get the
* source of the document.
* <li>Get the canonical domain using the
- * <a href="https://publicsuffix.org/>Public Suffix List</a> (see example below).
+ * <a href="https://publicsuffix.org/">Public Suffix List</a> (see example below).
* <li>Use <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a>
* to obtain the package name and certificate fingerprint of the package corresponding to
* the canonical domain.
diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java
index 4951237..3306a50 100644
--- a/core/java/android/transition/TransitionUtils.java
+++ b/core/java/android/transition/TransitionUtils.java
@@ -115,7 +115,7 @@
/**
* Get a copy of bitmap of given drawable, return null if intrinsic size is zero
*/
- public static Bitmap createDrawableBitmap(Drawable drawable) {
+ public static Bitmap createDrawableBitmap(Drawable drawable, View hostView) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
if (width <= 0 || height <= 0) {
@@ -128,7 +128,7 @@
}
int bitmapWidth = (int) (width * scale);
int bitmapHeight = (int) (height * scale);
- final RenderNode node = RenderNode.create("TransitionUtils", null);
+ final RenderNode node = RenderNode.create("TransitionUtils", hostView);
node.setLeftTopRightBottom(0, 0, width, height);
node.setClipToBounds(false);
final DisplayListCanvas canvas = node.start(width, height);
@@ -159,17 +159,20 @@
* @return A bitmap of the given view or null if bounds has no width or height.
*/
public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
+ if (!view.isAttachedToWindow()) {
+ return null;
+ }
Bitmap bitmap = null;
int bitmapWidth = Math.round(bounds.width());
int bitmapHeight = Math.round(bounds.height());
if (bitmapWidth > 0 && bitmapHeight > 0) {
- float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
+ float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
bitmapWidth *= scale;
bitmapHeight *= scale;
matrix.postTranslate(-bounds.left, -bounds.top);
matrix.postScale(scale, scale);
- final RenderNode node = RenderNode.create("TransitionUtils", null);
+ final RenderNode node = RenderNode.create("TransitionUtils", view);
node.setLeftTopRightBottom(0, 0, bitmapWidth, bitmapHeight);
node.setClipToBounds(false);
final DisplayListCanvas canvas = node.start(bitmapWidth, bitmapHeight);
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 8691136..0299865 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -392,7 +392,7 @@
// and the length of the tag.
// Note: we implicitly accept possible truncation for Modified-UTF8 differences. It
// is too expensive to compute that ahead of time.
- int bufferSize = NoPreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD // Base.
+ int bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD // Base.
- 2 // Two terminators.
- (tag != null ? tag.length() : 0) // Tag length.
- 32; // Some slack.
@@ -429,10 +429,10 @@
}
/**
- * NoPreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
+ * PreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
* a JNI call during logging.
*/
- static class NoPreloadHolder {
+ static class PreloadHolder {
public final static int LOGGER_ENTRY_MAX_PAYLOAD =
logger_entry_max_payload_native();
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index fceefaf..6c13e0c 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -70,14 +70,15 @@
mEditor = Preconditions.checkNotNull(editor);
mTextView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
- mTextView.getTextClassifier(), mTextView.getText(),
+ mTextView.getTextClassifier(),
+ getText(mTextView),
0, 1, mTextView.getTextLocales());
mSelectionTracker = new SelectionTracker(mTextView);
}
public void startActionModeAsync(boolean adjustSelection) {
mSelectionTracker.onOriginalSelection(
- mTextView.getText(),
+ getText(mTextView),
mTextView.getSelectionStart(),
mTextView.getSelectionEnd(),
mTextView.isTextEditable());
@@ -166,7 +167,7 @@
}
private void startActionMode(@Nullable SelectionResult result) {
- final CharSequence text = mTextView.getText();
+ final CharSequence text = getText(mTextView);
if (result != null && text instanceof Spannable) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
mTextClassification = result.mClassification;
@@ -198,7 +199,9 @@
}
private void resetTextClassificationHelper() {
- mTextClassificationHelper.reset(mTextView.getTextClassifier(), mTextView.getText(),
+ mTextClassificationHelper.reset(
+ mTextView.getTextClassifier(),
+ getText(mTextView),
mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
mTextView.getTextLocales());
}
@@ -301,7 +304,7 @@
if (isSelectionStarted()
&& mAllowReset
&& textIndex >= mSelectionStart && textIndex <= mSelectionEnd
- && textView.getText() instanceof Spannable) {
+ && getText(textView) instanceof Spannable) {
mAllowReset = false;
boolean selected = editor.selectCurrentWord();
if (selected) {
@@ -557,7 +560,7 @@
mSelectionResultSupplier = Preconditions.checkNotNull(selectionResultSupplier);
mSelectionResultCallback = Preconditions.checkNotNull(selectionResultCallback);
// Make a copy of the original text.
- mOriginalText = mTextView.getText().toString();
+ mOriginalText = getText(mTextView).toString();
}
@Override
@@ -573,7 +576,7 @@
@Override
@UiThread
protected void onPostExecute(SelectionResult result) {
- result = TextUtils.equals(mOriginalText, mTextView.getText()) ? result : null;
+ result = TextUtils.equals(mOriginalText, getText(mTextView)) ? result : null;
mSelectionResultCallback.accept(result);
}
@@ -702,8 +705,6 @@
}
}
-
-
@SelectionEvent.ActionType
private static int getActionType(int menuItemId) {
switch (menuItemId) {
@@ -724,4 +725,14 @@
return SelectionEvent.ActionType.OTHER;
}
}
+
+ private static CharSequence getText(TextView textView) {
+ // Extracts the textView's text.
+ // TODO: Investigate why/when TextView.getText() is null.
+ final CharSequence text = textView.getText();
+ if (text != null) {
+ return text;
+ }
+ return "";
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2cab009..eb639bd 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1450,11 +1450,16 @@
getFirstRowPosition(rowPosition + 1));
int serviceSpacing = holder.row.getContext().getResources()
.getDimensionPixelSize(R.dimen.chooser_service_spacing);
- int top = rowPosition == 0 ? serviceSpacing : 0;
- if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
- setVertPadding(holder, top, serviceSpacing);
+ if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ // if the row is the only row for target service
+ setVertPadding(holder, 0, 0);
} else {
- setVertPadding(holder, top, 0);
+ int top = rowPosition == 0 ? serviceSpacing : 0;
+ if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ setVertPadding(holder, top, serviceSpacing);
+ } else {
+ setVertPadding(holder, top, 0);
+ }
}
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 76b3528..ee5c758 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -70,6 +70,8 @@
android:textSize="16sp"
android:textColor="#eeffffff"
android:layout_marginTop="4dp"
+ android:ellipsize="end"
+ android:maxLines="7"
/>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 105ef44..9aca2de 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2056,11 +2056,11 @@
Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. -->
<attr name="navigationBarColor" format="color" />
- <!-- @hide
- Shows 1dp line of the specified color between the navigation bar and the app content.
+ <!-- Shows a thin line of the specified color between the navigation bar and the app
+ content.
<p>For this to take effect, the window must be drawing the system bar backgrounds with
{@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
- have been requested to be translucent with
+ have been requested to be translucent with
{@link android.R.attr#windowTranslucentNavigation}. -->
<attr name="navigationBarDividerColor" format="color" />
@@ -2097,7 +2097,7 @@
-->
<attr name="windowSplashscreenContent" format="reference" />
- <!-- @hide If set, the navigation bar will be drawn such that it is
+ <!-- If set, the navigation bar will be drawn such that it is
compatible with a light navigation bar background.
<p>For this to take effect, the window must be drawing the system bar backgrounds with
{@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9eed12f..b2727f8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3023,7 +3023,7 @@
<bool name="config_handleVolumeKeysInWindowManager">false</bool>
<!-- Volume level of in-call notification tone playback [0..1] -->
- <item name="config_inCallNotificationVolume" format="float" type="dimen">.25</item>
+ <item name="config_inCallNotificationVolume" format="float" type="dimen">.10</item>
<!-- URI for in call notification sound -->
<string translatable="false" name="config_inCallNotificationSound">/system/media/audio/ui/InCallNotification.ogg</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 634d79a..cfcd23d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2846,6 +2846,8 @@
<public name="showWhenLocked"/>
<public name="turnScreenOn"/>
<public name="classLoader" />
+ <public name="windowLightNavigationBar" />
+ <public name="navigationBarDividerColor" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ce6815f..5347719 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2679,6 +2679,8 @@
<string name="yes">OK</string>
<!-- Preference framework strings. -->
<string name="no">Cancel</string>
+ <!-- Preference framework strings. -->
+ <string name="close">CLOSE</string>
<!-- This is the generic "attention" string to be used in attention dialogs. Typically
combined with setIconAttribute(android.R.attr.alertDialogIcon)
(or setIcon(android.R.drawable.ic_dialog_alert) on legacy versions of the platform) -->
@@ -2811,6 +2813,11 @@
<!-- [CHAR LIMIT=200] Compat mode dialog: hint to re-enable compat mode dialog. -->
<string name="screen_compat_mode_hint">Re-enable this in System settings > Apps > Downloaded.</string>
+ <!-- Text of the alert that is displayed when a top application is killed by lmk. -->
+ <string name="top_app_killed_title">App isn\'t responding</string>
+ <!-- Top app killed by lmk dialog message. -->
+ <string name="top_app_killed_message"><xliff:g id="app_name">%1$s</xliff:g> may be using too much memory.</string>
+
<!-- [CHAR LIMIT=200] Unsupported display size dialog: message. Refers to "Display size" setting. -->
<string name="unsupported_display_size_message"><xliff:g id="app_name">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly.</string>
<!-- [CHAR LIMIT=50] Unsupported display size dialog: check box label. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0548db6..b36630e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1890,6 +1890,9 @@
<java-symbol type="string" name="anr_application_process" />
<java-symbol type="string" name="anr_process" />
<java-symbol type="string" name="anr_title" />
+ <java-symbol type="string" name="top_app_killed_title" />
+ <java-symbol type="string" name="top_app_killed_message" />
+ <java-symbol type="string" name="close" />
<java-symbol type="string" name="car_mode_disable_notification_message" />
<java-symbol type="string" name="car_mode_disable_notification_title" />
<java-symbol type="string" name="chooser_wallpaper" />
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 0209cea..40288f5 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -159,8 +159,10 @@
if (mNativeInstance == 0) {
mNativeInstance = createNativeInstance(mLocalMatrix == null
? 0 : mLocalMatrix.native_instance);
- mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
- this, mNativeInstance);
+ if (mNativeInstance != 0) {
+ mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
+ this, mNativeInstance);
+ }
}
return mNativeInstance;
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 013e85e..b4fff48 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -598,14 +598,20 @@
@Override
public void onRestoreRoute() {
- // Skip restoring route if the selected route is not a system audio route, or
- // MediaRouter is initializing.
- if ((mSelectedRoute != mDefaultAudioVideo && mSelectedRoute != mBluetoothA2dpRoute)
- || mSelectedRoute == null) {
- return;
- }
- Log.v(TAG, "onRestoreRoute() : a2dp=" + isBluetoothA2dpOn());
- mSelectedRoute.select();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Skip restoring route if the selected route is not a system audio route,
+ // MediaRouter is initializing, or mClient was changed.
+ if (Client.this != mClient || mSelectedRoute == null
+ || (mSelectedRoute != mDefaultAudioVideo
+ && mSelectedRoute != mBluetoothA2dpRoute)) {
+ return;
+ }
+ Log.v(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
+ mSelectedRoute.select();
+ }
+ });
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
index b7fd404..3c5ac8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -73,7 +73,7 @@
final Drawable deviceDrawable = context.getDrawable(resId);
final BatteryMeterDrawable batteryDrawable = new BatteryMeterDrawable(context,
- R.color.meter_background_color, batteryLevel);
+ context.getColor(R.color.meter_background_color), batteryLevel);
final int pad = context.getResources().getDimensionPixelSize(R.dimen.bt_battery_padding);
batteryDrawable.setPadding(pad, pad, pad, pad);
@@ -107,6 +107,8 @@
@VisibleForTesting
static class BatteryMeterDrawable extends BatteryMeterDrawableBase {
private final float mAspectRatio;
+ @VisibleForTesting
+ int mFrameColor;
public BatteryMeterDrawable(Context context, int frameColor, int batteryLevel) {
super(context, frameColor);
@@ -118,6 +120,7 @@
final int tintColor = Utils.getColorAttr(context, android.R.attr.colorControlNormal);
setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN));
setBatteryLevel(batteryLevel);
+ mFrameColor = frameColor;
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 76760a9..adec402 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -100,4 +100,15 @@
assertThat(twinDrawable.getLayerInsetTop(1)).isEqualTo(
drawable.getLayerInsetTop(1));
}
+
+ @Test
+ public void testCreateLayerDrawable_bluetoothDrawable_hasCorrectFrameColor() {
+ BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
+ mContext, RES_ID, BATTERY_LEVEL);
+ BluetoothDeviceLayerDrawable.BatteryMeterDrawable batteryMeterDrawable =
+ (BluetoothDeviceLayerDrawable.BatteryMeterDrawable) drawable.getDrawable(1);
+
+ assertThat(batteryMeterDrawable.mFrameColor).isEqualTo(
+ mContext.getColor(R.color.meter_background_color));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 5c99961..debda21 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -16,8 +16,13 @@
package com.android.systemui.doze;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
@@ -34,6 +39,10 @@
public class AlwaysOnDisplayPolicy {
public static final String TAG = "AlwaysOnDisplayPolicy";
+ private static final long DEFAULT_PROX_SCREEN_OFF_DELAY_MS = 10 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_PROX_COOLDOWN_TRIGGER_MS = 2 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_PROX_COOLDOWN_PERIOD_MS = 5 * DateUtils.SECOND_IN_MILLIS;
+
static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array";
static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array";
static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay";
@@ -46,7 +55,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_SCREEN_BRIGHTNESS_ARRAY
*/
- public final int[] screenBrightnessArray;
+ public int[] screenBrightnessArray;
/**
* Integer array to map ambient brightness type to dimming scrim.
@@ -54,7 +63,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_DIMMING_SCRIM_ARRAY
*/
- public final int[] dimmingScrimArray;
+ public int[] dimmingScrimArray;
/**
* Delay time(ms) from covering the prox to turning off the screen.
@@ -62,7 +71,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_SCREEN_OFF_DELAY_MS
*/
- public final long proxScreenOffDelayMs;
+ public long proxScreenOffDelayMs;
/**
* The threshold time(ms) to trigger the cooldown timer, which will
@@ -71,7 +80,7 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_COOLDOWN_TRIGGER_MS
*/
- public final long proxCooldownTriggerMs;
+ public long proxCooldownTriggerMs;
/**
* The period(ms) to turning off the prox sensor if
@@ -80,43 +89,78 @@
* @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
* @see #KEY_PROX_COOLDOWN_PERIOD_MS
*/
- public final long proxCooldownPeriodMs;
+ public long proxCooldownPeriodMs;
private final KeyValueListParser mParser;
+ private final Context mContext;
+ private SettingsObserver mSettingsObserver;
public AlwaysOnDisplayPolicy(Context context) {
- final Resources resources = context.getResources();
+ mContext = context;
mParser = new KeyValueListParser(',');
-
- final String value = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
-
- try {
- mParser.setString(value);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Bad AOD constants");
- }
-
- proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
- 10 * DateUtils.SECOND_IN_MILLIS);
- proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
- 2 * DateUtils.SECOND_IN_MILLIS);
- proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
- 5 * DateUtils.SECOND_IN_MILLIS);
- screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
- resources.getIntArray(R.array.config_doze_brightness_sensor_to_brightness));
- dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
- resources.getIntArray(R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ mSettingsObserver = new SettingsObserver(context.getMainThreadHandler());
+ mSettingsObserver.observe();
}
private int[] parseIntArray(final String key, final int[] defaultArray) {
final String value = mParser.getString(key, null);
if (value != null) {
- return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
- Integer::parseInt).toArray();
+ try {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } catch (NumberFormatException e) {
+ return defaultArray;
+ }
} else {
return defaultArray;
}
}
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri ALWAYS_ON_DISPLAY_CONSTANTS_URI
+ = Settings.Global.getUriFor(Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI,
+ false, this, UserHandle.USER_ALL);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ public void update(Uri uri) {
+ if (uri == null || ALWAYS_ON_DISPLAY_CONSTANTS_URI.equals(uri)) {
+ final Resources resources = mContext.getResources();
+ final String value = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ try {
+ mParser.setString(value);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad AOD constants");
+ }
+
+ proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
+ DEFAULT_PROX_SCREEN_OFF_DELAY_MS);
+ proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
+ DEFAULT_PROX_COOLDOWN_TRIGGER_MS);
+ proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
+ DEFAULT_PROX_COOLDOWN_PERIOD_MS);
+ screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+ resources.getIntArray(
+ R.array.config_doze_brightness_sensor_to_brightness));
+ dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+ resources.getIntArray(
+ R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
index 76a1902..58f1448 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
@@ -28,20 +28,21 @@
public static final String TAG = DozePauser.class.getSimpleName();
private final AlarmTimeout mPauseTimeout;
private final DozeMachine mMachine;
- private final long mTimeoutMs;
+ private final AlwaysOnDisplayPolicy mPolicy;
public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
AlwaysOnDisplayPolicy policy) {
mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
- mTimeoutMs = policy.proxScreenOffDelayMs;
+ mPolicy = policy;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
- mPauseTimeout.schedule(mTimeoutMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mPauseTimeout.schedule(mPolicy.proxScreenOffDelayMs,
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
break;
default:
mPauseTimeout.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 50720e9..b5c0d53 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -29,6 +29,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.LinkedList;
/**
@@ -57,8 +59,12 @@
}
}
- private LinkedList<Command> mCmdQueue = new LinkedList();
+ private final LinkedList<Command> mCmdQueue = new LinkedList<Command>();
+ private final Object mCompletionHandlingLock = new Object();
+ @GuardedBy("mCompletionHandlingLock")
+ private CreationAndCompletionThread mCompletionThread;
+ @GuardedBy("mCompletionHandlingLock")
private Looper mLooper;
/*
@@ -76,7 +82,10 @@
public void run() {
Looper.prepare();
+ // ok to modify mLooper as here we are
+ // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
mLooper = Looper.myLooper();
+ if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
@@ -97,7 +106,7 @@
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (!audioManager.isMusicActiveRemotely()) {
- synchronized(mQueueAudioFocusLock) {
+ synchronized (mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus == null) {
if (DEBUG) Log.d(mTag, "requesting AudioFocus");
int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
@@ -129,7 +138,9 @@
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
+ if (DEBUG) { Log.d(mTag, "player.start"); }
if (mPlayer != null) {
+ if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
mPlayer.release();
}
mPlayer = player;
@@ -148,7 +159,7 @@
// is playing, let it continue until we're done, so there
// is less of a glitch.
try {
- if (DEBUG) Log.d(mTag, "Starting playback");
+ if (DEBUG) { Log.d(mTag, "startSound()"); }
//-----------------------------------
// This is were we deviate from the AsyncPlayer implementation and create the
// MediaPlayer in a new thread with which we're synchronized
@@ -158,10 +169,11 @@
// matters
if((mLooper != null)
&& (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+ if (DEBUG) { Log.d(mTag, "in startSound quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = new CreationAndCompletionThread(cmd);
- synchronized(mCompletionThread) {
+ synchronized (mCompletionThread) {
mCompletionThread.start();
mCompletionThread.wait();
}
@@ -209,13 +221,18 @@
mPlayer = null;
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
+ if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
}
}
- if((mLooper != null)
- && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
- mLooper.quit();
+ synchronized (mCompletionHandlingLock) {
+ if ((mLooper != null) &&
+ (mLooper.getThread().getState() != Thread.State.TERMINATED))
+ {
+ if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
+ mLooper.quit();
+ }
}
} else {
Log.w(mTag, "STOP command without a player");
@@ -250,9 +267,11 @@
}
// if there are no more sounds to play, end the Looper to listen for media completion
synchronized (mCmdQueue) {
- if (mCmdQueue.size() == 0) {
- synchronized(mCompletionHandlingLock) {
- if(mLooper != null) {
+ synchronized(mCompletionHandlingLock) {
+ if (DEBUG) { Log.d(mTag, "onCompletion queue size=" + mCmdQueue.size()); }
+ if ((mCmdQueue.size() == 0)) {
+ if (mLooper != null) {
+ if (DEBUG) { Log.d(mTag, "in onCompletion quitting looper " + mLooper); }
mLooper.quit();
}
mCompletionThread = null;
@@ -269,13 +288,20 @@
}
private String mTag;
+
+ @GuardedBy("mCmdQueue")
private CmdThread mThread;
- private CreationAndCompletionThread mCompletionThread;
- private final Object mCompletionHandlingLock = new Object();
+
private MediaPlayer mPlayer;
+
+
+ @GuardedBy("mCmdQueue")
private PowerManager.WakeLock mWakeLock;
+
private final Object mQueueAudioFocusLock = new Object();
- private AudioManager mAudioManagerWithAudioFocus; // synchronized on mQueueAudioFocusLock
+ @GuardedBy("mQueueAudioFocusLock")
+ private AudioManager mAudioManagerWithAudioFocus;
+
private int mNotificationRampTimeMs = 0;
// The current state according to the caller. Reality lags behind
@@ -311,6 +337,7 @@
*/
@Deprecated
public void play(Context context, Uri uri, boolean looping, int stream) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
@@ -339,6 +366,7 @@
* (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
*/
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
+ if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
@@ -357,6 +385,7 @@
* at this point. Calling this multiple times has no ill effects.
*/
public void stop() {
+ if (DEBUG) { Log.d(mTag, "stop"); }
synchronized (mCmdQueue) {
// This check allows stop to be called multiple times without starting
// a thread that ends up doing nothing.
@@ -370,6 +399,7 @@
}
}
+ @GuardedBy("mCmdQueue")
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);
if (mThread == null) {
@@ -393,22 +423,26 @@
* @hide
*/
public void setUsesWakeLock(Context context) {
- if (mWakeLock != null || mThread != null) {
- // if either of these has happened, we've already played something.
- // and our releases will be out of sync.
- throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
- + " mThread=" + mThread);
+ synchronized (mCmdQueue) {
+ if (mWakeLock != null || mThread != null) {
+ // if either of these has happened, we've already played something.
+ // and our releases will be out of sync.
+ throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock
+ + " mThread=" + mThread);
+ }
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
}
+ @GuardedBy("mCmdQueue")
private void acquireWakeLock() {
if (mWakeLock != null) {
mWakeLock.acquire();
}
}
+ @GuardedBy("mCmdQueue")
private void releaseWakeLock() {
if (mWakeLock != null) {
mWakeLock.release();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 8d62f2a..81b8622 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -23,6 +23,7 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
@@ -34,10 +35,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.R.drawable;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -135,11 +134,7 @@
if (lastDevice != null) {
int batteryLevel = lastDevice.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext,
- R.drawable.ic_qs_bluetooth_connected, batteryLevel,
- mContext.getResources().getFraction(
- R.fraction.bt_battery_scale_fraction, 1, 1));
- state.icon = new DrawableIcon(drawable);
+ state.icon = new BluetoothBatteryDrawable(batteryLevel);
}
}
state.contentDescription = mContext.getString(
@@ -215,6 +210,22 @@
return new BluetoothDetailAdapter();
}
+ private class BluetoothBatteryDrawable extends Icon {
+ private int mLevel;
+
+ BluetoothBatteryDrawable(int level) {
+ mLevel = level;
+ }
+
+ @Override
+ public Drawable getDrawable(Context context) {
+ return createLayerDrawable(context,
+ R.drawable.ic_qs_bluetooth_connected, mLevel,
+ context.getResources().getFraction(
+ R.fraction.bt_battery_scale_fraction, 1, 1));
+ }
+ }
+
protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
// We probably won't ever have space in the UI for more than 20 devices, so don't
// get info for them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 0bb7479..0b0f58b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -263,7 +263,7 @@
}
@Override
- public void setNoSims(boolean show) {
+ public void setNoSims(boolean show, boolean simDetected) {
mInfo.noSim = show;
if (mInfo.noSim) {
// Make sure signal gets cleared out when no sims.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
index e02fb14..e4a4f59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -22,5 +22,15 @@
* This is sent when the stack action button should be hidden.
*/
public class HideStackActionButtonEvent extends EventBus.Event {
- // Simple event
+
+ // Whether or not to translate the stack action button when hiding it
+ public final boolean translate;
+
+ public HideStackActionButtonEvent() {
+ this(true);
+ }
+
+ public HideStackActionButtonEvent(boolean translate) {
+ this.translate = translate;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index c44cd72..4a11fd05 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -174,8 +174,6 @@
? R.layout.recents_low_ram_stack_action_button
: R.layout.recents_stack_action_button,
this, false);
- mStackActionButton.setOnClickListener(
- v -> EventBus.getDefault().send(new DismissAllTaskViewsEvent()));
mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
@@ -572,7 +570,7 @@
mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
event.taskView, event.screenPinningRequested, event.targetTaskStack);
if (Recents.getConfiguration().isLowRamDevice) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, false /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
}
}
@@ -580,7 +578,7 @@
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
if (RecentsDebugFlags.Static.EnableStackActionButton) {
// Hide the stack action button
- hideStackActionButton(taskViewExitToHomeDuration, false /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
animateBackgroundScrim(0f, taskViewExitToHomeDuration);
@@ -741,13 +739,10 @@
animateBackgroundScrim(getOpaqueScrimAlpha(),
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
- if (Recents.getConfiguration().isLowRamDevice && mEmptyView.getVisibility() != View.VISIBLE) {
- showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, false /* translate */);
- }
}
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
+ EventBus.getDefault().send(new HideStackActionButtonEvent());
}
public final void onBusEvent(DismissAllTaskViewsEvent event) {
@@ -795,7 +790,8 @@
mStackActionButton.setVisibility(View.VISIBLE);
mStackActionButton.setAlpha(0f);
if (translate) {
- mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() *
+ (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
} else {
mStackActionButton.setTranslationY(0f);
}
@@ -841,8 +837,8 @@
if (mStackActionButton.getVisibility() == View.VISIBLE) {
if (translate) {
- mStackActionButton.animate()
- .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
+ mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
+ * (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f));
}
mStackActionButton.animate()
.alpha(0f)
@@ -954,7 +950,7 @@
/**
* @return the bounds of the stack action button.
*/
- private Rect getStackActionButtonBoundsFromStackLayout() {
+ Rect getStackActionButtonBoundsFromStackLayout() {
Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
int left, top;
if (Recents.getConfiguration().isLowRamDevice) {
@@ -976,6 +972,16 @@
return actionButtonRect;
}
+ View getStackActionButton() {
+ return mStackActionButton;
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ mTouchHandler.cancelStackActionButtonClick();
+ }
+
public void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
String id = Integer.toHexString(System.identityHashCode(this));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 46619c2..b6b24bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -23,6 +23,7 @@
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
@@ -31,6 +32,8 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
@@ -99,8 +102,7 @@
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
- handleTouchEvent(ev);
- return mDragRequested;
+ return handleTouchEvent(ev) || mDragRequested;
}
/** Handles touch events once we have intercepted them */
@@ -183,22 +185,47 @@
}
}
+ void cancelStackActionButtonClick() {
+ mRv.getStackActionButton().setPressed(false);
+ }
+
+ private boolean isWithinStackActionButton(float x, float y) {
+ Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+ return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
+ mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
+ }
+
+ private void changeStackActionButtonDrawableHotspot(float x, float y) {
+ Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
+ mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
+ }
+
/**
* Handles dragging touch events
*/
- private void handleTouchEvent(MotionEvent ev) {
+ private boolean handleTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ boolean consumed = false;
+ float evX = ev.getX();
+ float evY = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
- mDownPos.set((int) ev.getX(), (int) ev.getY());
+ mDownPos.set((int) evX, (int) evY);
mDeviceId = ev.getDeviceId();
+
+ if (isWithinStackActionButton(evX, evY)) {
+ changeStackActionButtonDrawableHotspot(evX, evY);
+ mRv.getStackActionButton().setPressed(true);
+ }
break;
case MotionEvent.ACTION_MOVE: {
- float evX = ev.getX();
- float evY = ev.getY();
float x = evX - mTaskViewOffset.x;
float y = evY - mTaskViewOffset.y;
+ if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+ changeStackActionButtonDrawableHotspot(evX, evY);
+ }
+
if (mDragRequested) {
if (!mIsDragging) {
mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
@@ -232,9 +259,7 @@
EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
currentDropTarget));
}
-
}
-
mTaskView.setTranslationX(x);
mTaskView.setTranslationY(y);
}
@@ -242,6 +267,11 @@
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
+ EventBus.getDefault().send(new DismissAllTaskViewsEvent());
+ consumed = true;
+ }
+ cancelStackActionButtonClick();
if (mDragRequested) {
boolean cancelled = action == MotionEvent.ACTION_CANCEL;
if (cancelled) {
@@ -254,5 +284,6 @@
mDeviceId = -1;
}
}
+ return consumed;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 8899e30..d32b220 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1768,8 +1768,17 @@
}
// In grid layout, the stack action button always remains visible.
- if (mEnterAnimationComplete && !useGridLayout() &&
- !Recents.getConfiguration().isLowRamDevice) {
+ if (mEnterAnimationComplete && !useGridLayout()) {
+ if (Recents.getConfiguration().isLowRamDevice) {
+ // Show stack button when user drags down to show older tasks on low ram devices
+ if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible
+ && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) {
+ // Going up
+ EventBus.getDefault().send(
+ new ShowStackActionButtonEvent(true /* translate */));
+ }
+ return;
+ }
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
mStack.getTaskCount() > 0) {
@@ -1956,6 +1965,9 @@
// Remove the task from the stack
mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
+ if (mStack.getTaskCount() > 0 && Recents.getConfiguration().isLowRamDevice) {
+ EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
+ }
MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
event.task.key.getComponent().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
index bcf4f17..2d7cfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
@@ -26,8 +26,8 @@
public class GridTaskViewThumbnail extends TaskViewThumbnail {
- private Path mThumbnailOutline;
- private Path mRestBackgroundOutline;
+ private final Path mThumbnailOutline = new Path();
+ private final Path mRestBackgroundOutline = new Path();
// True if either this view's size or thumbnail scale has changed and mThumbnailOutline should
// be updated.
private boolean mUpdateThumbnailOutline = true;
@@ -77,47 +77,38 @@
(int) (mThumbnailRect.width() * mThumbnailScale));
final int thumbnailHeight = Math.min(viewHeight,
(int) (mThumbnailRect.height() * mThumbnailScale));
- // Draw the thumbnail, we only round the bottom corners:
- //
- // outerLeft outerRight
- // <-----------------------> mRestBackgroundOutline
- // _________________________ (thumbnailWidth < viewWidth)
- // |_______________________| outerTop A ____ B
- // | | ↑ | |
- // | | | | |
- // | | | | |
- // | | | | | C
- // \_______________________/ ↓ |__/
- // mCornerRadius outerBottom E D
- //
- // mRestBackgroundOutline (thumbnailHeight < viewHeight)
- // A _________________________ B
- // | | C
- // F \_______________________/
- // E D
- final int outerLeft = 0;
- final int outerTop = 0;
- final int outerRight = outerLeft + thumbnailWidth;
- final int outerBottom = outerTop + thumbnailHeight;
- mThumbnailOutline = new Path();
- mThumbnailOutline.moveTo(outerLeft, outerTop);
- mThumbnailOutline.lineTo(outerRight, outerTop);
- mThumbnailOutline.lineTo(outerRight, outerBottom - mCornerRadius);
- mThumbnailOutline.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius,
- outerRight, outerBottom, 0, 90, false);
- mThumbnailOutline.lineTo(outerLeft + mCornerRadius, outerBottom);
- mThumbnailOutline.arcTo(outerLeft, outerBottom - 2 * mCornerRadius,
- outerLeft + 2 * mCornerRadius, outerBottom, 90, 90, false);
- mThumbnailOutline.lineTo(outerLeft, outerTop);
- mThumbnailOutline.close();
if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+ // Draw the thumbnail, we only round the bottom corners:
+ //
+ // outerLeft outerRight
+ // <-----------------------> mRestBackgroundOutline
+ // _________________________ (thumbnailWidth < viewWidth)
+ // |_______________________| outerTop A ____ B
+ // | | ↑ | |
+ // | | | | |
+ // | | | | |
+ // | | | | | C
+ // \_______________________/ ↓ |__/
+ // mCornerRadius outerBottom E D
+ //
+ // mRestBackgroundOutline (thumbnailHeight < viewHeight)
+ // A _________________________ B
+ // | | C
+ // F \_______________________/
+ // E D
+ final int outerLeft = 0;
+ final int outerTop = 0;
+ final int outerRight = outerLeft + thumbnailWidth;
+ final int outerBottom = outerTop + thumbnailHeight;
+ createThumbnailPath(outerLeft, outerTop, outerRight, outerBottom, mThumbnailOutline);
+
if (thumbnailWidth < viewWidth) {
final int l = Math.max(0, outerRight - mCornerRadius);
final int r = outerRight;
final int t = outerTop;
final int b = outerBottom;
- mRestBackgroundOutline = new Path();
+ mRestBackgroundOutline.reset();
mRestBackgroundOutline.moveTo(l, t); // A
mRestBackgroundOutline.lineTo(r, t); // B
mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
@@ -133,7 +124,7 @@
final int r = outerRight;
final int t = Math.max(0, thumbnailHeight - mCornerRadius);
final int b = outerBottom;
- mRestBackgroundOutline = new Path();
+ mRestBackgroundOutline.reset();
mRestBackgroundOutline.moveTo(l, t); // A
mRestBackgroundOutline.lineTo(r, t); // B
mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
@@ -145,9 +136,26 @@
mRestBackgroundOutline.lineTo(l, t); // A
mRestBackgroundOutline.close();
}
+ } else {
+ createThumbnailPath(0, 0, viewWidth, viewHeight, mThumbnailOutline);
}
}
+ private void createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom,
+ Path outPath) {
+ outPath.reset();
+ outPath.moveTo(outerLeft, outerTop);
+ outPath.lineTo(outerRight, outerTop);
+ outPath.lineTo(outerRight, outerBottom - mCornerRadius);
+ outPath.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, outerRight,
+ outerBottom, 0, 90, false);
+ outPath.lineTo(outerLeft + mCornerRadius, outerBottom);
+ outPath.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, outerLeft + 2 * mCornerRadius,
+ outerBottom, 90, 90, false);
+ outPath.lineTo(outerLeft, outerTop);
+ outPath.close();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
final int titleHeight = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6bfef20..23a7dae 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -64,6 +64,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
@@ -1210,6 +1211,10 @@
}
}
+ public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+ saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ }
+
public final void onBusEvent(DockedTopTaskEvent event) {
if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
mState.growAfterRecentsDrawn = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 759d2cf..274244e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -22,8 +22,6 @@
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.telephony.SubscriptionInfo;
import android.util.ArraySet;
@@ -53,6 +51,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
// Intimately tied to the design of res/layout/signal_cluster_view.xml
public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
@@ -73,6 +72,7 @@
private boolean mNoSimsVisible = false;
private boolean mVpnVisible = false;
+ private boolean mSimDetected;
private int mVpnIconId = 0;
private int mLastVpnIconId = -1;
private boolean mEthernetVisible = false;
@@ -327,8 +327,9 @@
}
@Override
- public void setNoSims(boolean show) {
+ public void setNoSims(boolean show, boolean simDetected) {
mNoSimsVisible = show && !mBlockMobile;
+ mSimDetected = simDetected;
apply();
}
@@ -548,6 +549,23 @@
if (mNoSimsVisible) {
mIconLogger.onIconShown(SLOT_MOBILE);
mNoSimsCombo.setVisibility(View.VISIBLE);
+ if (!Objects.equals(mSimDetected, mNoSimsCombo.getTag())) {
+ mNoSimsCombo.setTag(mSimDetected);
+ if (mSimDetected) {
+ SignalDrawable d = new SignalDrawable(mNoSims.getContext());
+ d.setDarkIntensity(0);
+ mNoSims.setImageDrawable(d);
+ mNoSims.setImageLevel(SignalDrawable.getEmptyState(4));
+
+ SignalDrawable dark = new SignalDrawable(mNoSims.getContext());
+ dark.setDarkIntensity(1);
+ mNoSimsDark.setImageDrawable(dark);
+ mNoSimsDark.setImageLevel(SignalDrawable.getEmptyState(4));
+ } else {
+ mNoSims.setImageResource(R.drawable.stat_sys_no_sims);
+ mNoSimsDark.setImageResource(R.drawable.stat_sys_no_sims);
+ }
+ }
} else {
mIconLogger.onIconHidden(SLOT_MOBILE);
mNoSimsCombo.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index a456786..5159e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -71,7 +71,7 @@
break;
case MSG_NO_SIM_VISIBLE_CHANGED:
for (SignalCallback signalCluster : mSignalCallbacks) {
- signalCluster.setNoSims(msg.arg1 != 0);
+ signalCluster.setNoSims(msg.arg1 != 0, msg.arg2 != 0);
}
break;
case MSG_ETHERNET_CHANGED:
@@ -144,8 +144,8 @@
}
@Override
- public void setNoSims(boolean show) {
- obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, 0).sendToTarget();
+ public void setNoSims(boolean show, boolean simDetected) {
+ obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 2771011..9eee906 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -52,7 +52,7 @@
int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
String description, boolean isWide, int subId, boolean roaming) {}
default void setSubs(List<SubscriptionInfo> subs) {}
- default void setNoSims(boolean show) {}
+ default void setNoSims(boolean show, boolean simDetected) {}
default void setEthernetIndicators(IconState icon) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index c217bda..bb3e09f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -58,10 +58,8 @@
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -116,7 +114,7 @@
// States that don't belong to a subcontroller.
private boolean mAirplaneMode = false;
- private boolean mHasNoSims;
+ private boolean mHasNoSubs;
private Locale mLocale = null;
// This list holds our ordering.
private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
@@ -140,6 +138,7 @@
@VisibleForTesting
ServiceState mLastServiceState;
private boolean mUserSetup;
+ private boolean mSimDetected;
/**
* Construct this controller object and register for updates.
@@ -363,7 +362,7 @@
cb.setSubs(mCurrentSubscriptions);
cb.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- cb.setNoSims(mHasNoSims);
+ cb.setNoSims(mHasNoSubs, mSimDetected);
mWifiSignalController.notifyListeners(cb);
mEthernetSignalController.notifyListeners(cb);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -498,13 +497,27 @@
@VisibleForTesting
protected void updateNoSims() {
- boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
- if (hasNoSims != mHasNoSims) {
- mHasNoSims = hasNoSims;
- mCallbackHandler.setNoSims(mHasNoSims);
+ boolean hasNoSubs = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
+ boolean simDetected = hasAnySim();
+ if (hasNoSubs != mHasNoSubs || simDetected != mSimDetected) {
+ mHasNoSubs = hasNoSubs;
+ mSimDetected = simDetected;
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
}
+ private boolean hasAnySim() {
+ int simCount = mPhone.getSimCount();
+ for (int i = 0; i < simCount; i++) {
+ int state = mPhone.getSimState(i);
+ if (state != TelephonyManager.SIM_STATE_ABSENT
+ && state != TelephonyManager.SIM_STATE_UNKNOWN) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@VisibleForTesting
void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
@@ -631,7 +644,7 @@
private void notifyListeners() {
mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
- mCallbackHandler.setNoSims(mHasNoSims);
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
/**
@@ -822,8 +835,8 @@
}
String nosim = args.getString("nosim");
if (nosim != null) {
- mHasNoSims = nosim.equals("show");
- mCallbackHandler.setNoSims(mHasNoSims);
+ mHasNoSubs = nosim.equals("show");
+ mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
String mobile = args.getString("mobile");
if (mobile != null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 51bd7bc..e3558d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -161,12 +161,11 @@
@Test
public void testSignalCallback_setNoSims() {
boolean noSims = true;
- mHandler.setNoSims(noSims);
+ boolean simDetected = false;
+ mHandler.setNoSims(noSims, simDetected);
waitForCallbacks();
- ArgumentCaptor<Boolean> noSimsArg = ArgumentCaptor.forClass(Boolean.class);
- Mockito.verify(mSignalCallback).setNoSims(noSimsArg.capture());
- assertEquals(noSims, (boolean) noSimsArg.getValue());
+ Mockito.verify(mSignalCallback).setNoSims(eq(noSims), eq(simDetected));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index bb7b197..d14b23e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -46,8 +46,6 @@
import org.junit.runner.Description;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -292,10 +290,8 @@
}
protected void verifyHasNoSims(boolean hasNoSimsVisible) {
- ArgumentCaptor<Boolean> hasNoSimsArg = ArgumentCaptor.forClass(Boolean.class);
-
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(hasNoSimsArg.capture());
- assertEquals("No sims", hasNoSimsVisible, (boolean) hasNoSimsArg.getValue());
+ Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(
+ eq(hasNoSimsVisible), eq(false));
}
protected void verifyLastQsMobileDataIndicators(boolean visible, int icon, int typeIcon,
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 28bf856..54eba2b 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -315,6 +315,27 @@
// Pno scan metrics
optional PnoScanMetrics pno_scan_metrics = 76;
+
+ // Histogram of "Connect to Network" notifications.
+ // The notification Action should be unset.
+ repeated ConnectToNetworkNotificationAndActionCount connect_to_network_notification_count = 77;
+
+ // Histogram of "Connect to Network" notification user actions.
+ repeated ConnectToNetworkNotificationAndActionCount connect_to_network_notification_action_count = 78;
+
+ // The number of SSIDs blacklisted from recommendation by the open network
+ // notification recommender
+ optional int32 open_network_recommender_blacklist_size = 79;
+
+ // Is the available network notification feature turned on
+ optional bool is_wifi_networks_available_notification_on = 80;
+
+ // Count of recommendation updates made by the open network notification
+ // recommender
+ optional int32 num_open_network_recommendation_updates = 81;
+
+ // Count of connection attempts that were initiated unsuccessfully
+ optional int32 num_open_network_connect_message_failed_to_send = 82;
}
// Information that gets logged for every WiFi connection.
@@ -950,3 +971,68 @@
// Total number of pno scans that found any network
optional int32 num_pno_found_network_events = 5;
}
+
+// Number of occurrences for a particular "Connect to Network" Notification or
+// notification Action.
+message ConnectToNetworkNotificationAndActionCount {
+
+ // "Connect to Network" notifications
+ enum Notification {
+
+ // Default
+ NOTIFICATION_UNKNOWN = 0;
+
+ // Initial notification with a recommended network.
+ NOTIFICATION_RECOMMEND_NETWORK = 1;
+
+ // Notification when connecting to the recommended network.
+ NOTIFICATION_CONNECTING_TO_NETWORK = 2;
+
+ // Notification when successfully connected to the network.
+ NOTIFICATION_CONNECTED_TO_NETWORK = 3;
+
+ // Notification when failed to connect to network.
+ NOTIFICATION_FAILED_TO_CONNECT = 4;
+ }
+
+ // "Connect to Network" notification actions
+ enum Action {
+
+ // Default
+ ACTION_UNKNOWN = 0;
+
+ // User dismissed the "Connect to Network" notification.
+ ACTION_USER_DISMISSED_NOTIFICATION = 1;
+
+ // User tapped action button to connect to recommended network.
+ ACTION_CONNECT_TO_NETWORK = 2;
+
+ // User tapped action button to open Wi-Fi Settings.
+ ACTION_PICK_WIFI_NETWORK = 3;
+
+ // User tapped "Failed to connect" notification to open Wi-Fi Settings.
+ ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE = 4;
+ }
+
+ // Recommenders of the "Connect to Network" notification
+ enum Recommender {
+
+ // Default.
+ RECOMMENDER_UNKNOWN = 0;
+
+ // Open Network Available recommender.
+ RECOMMENDER_OPEN = 1;
+ }
+
+ // Notification Type.
+ optional Notification notification = 1;
+
+ // Action Type.
+ optional Action action = 2;
+
+ // Recommender Type.
+ optional Recommender recommender = 3;
+
+ // Occurrences of this action.
+ optional int32 count = 4;
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2b4f0f3..9afa825 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -90,6 +91,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -781,6 +783,13 @@
mNetworksDefined++; // used only in the log() statement below.
}
+ // Do the same for Ethernet, since it's often not specified in the configs, although many
+ // devices can use it via USB host adapters.
+ if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+ mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
+ mNetworksDefined++;
+ }
+
if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
@@ -5546,6 +5555,11 @@
return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
}
+ @VisibleForTesting
+ public boolean hasService(String name) {
+ return ServiceManager.checkService(name) != null;
+ }
+
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
int newNetid = NETID_UNSET;
int prevNetid = NETID_UNSET;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cbfc747..30ecbcf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1690,6 +1690,7 @@
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
static final int START_USER_SWITCH_FG_MSG = 712;
+ static final int TOP_APP_KILLED_BY_LMK_MSG = 73;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1921,6 +1922,17 @@
dispatchProcessDied(pid, uid);
break;
}
+ case TOP_APP_KILLED_BY_LMK_MSG: {
+ final String appName = (String) msg.obj;
+ final AlertDialog d = new BaseErrorDialog(mUiContext);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.setTitle(mUiContext.getText(R.string.top_app_killed_title));
+ d.setMessage(mUiContext.getString(R.string.top_app_killed_message, appName));
+ d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.close),
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+ d.show();
+ break;
+ }
case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
@@ -5430,6 +5442,7 @@
boolean doLowMem = app.instr == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
+ maybeNotifyTopAppKilled(app);
Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died: "
+ ProcessList.makeOomAdjString(app.setAdj)
+ ProcessList.makeProcStateString(app.setProcState));
@@ -5463,6 +5476,23 @@
}
}
+ /** Show system error dialog when a top app is killed by LMK */
+ void maybeNotifyTopAppKilled(ProcessRecord app) {
+ if (!shouldNotifyTopAppKilled(app)) {
+ return;
+ }
+
+ Message msg = mHandler.obtainMessage(TOP_APP_KILLED_BY_LMK_MSG);
+ msg.obj = mContext.getPackageManager().getApplicationLabel(app.info);
+ mUiHandler.sendMessage(msg);
+ }
+
+ /** Only show notification when the top app is killed on low ram devices */
+ private boolean shouldNotifyTopAppKilled(ProcessRecord app) {
+ return app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+ ActivityManager.isLowRamDeviceStatic();
+ }
+
/**
* If a stack trace dump file is configured, dump process stack traces.
* @param clearTraces causes the dump file to be erased prior to the new
@@ -24537,7 +24567,7 @@
if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, 0 /*flags*/, app.userId);
+ .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
if (ai != null) {
app.thread.scheduleApplicationInfoChanged(ai);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a9ce927..148ce08 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2246,14 +2246,7 @@
try {
// Protect against recursion.
mStackSupervisor.inResumeTopActivity = true;
- // The contained logic must be synchronized, since we are both changing the visibility
- // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will
- // ultimately cause the client code to schedule a layout. Since layouts retrieve the
- // current {@link Configuration}, we must ensure that the below code updates it before
- // the layout can occur.
- synchronized (mWindowManager.getWindowManagerLock()) {
- result = resumeTopActivityInnerLocked(prev, options);
- }
+ result = resumeTopActivityInnerLocked(prev, options);
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
@@ -2564,128 +2557,139 @@
|| (lastStack.mLastPausedActivity != null
&& !lastStack.mLastPausedActivity.fullscreen));
- // This activity is now becoming visible.
- if (!next.visible || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
-
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
-
- ActivityRecord lastResumedActivity =
- lastStack == null ? null :lastStack.mResumedActivity;
- ActivityState lastState = next.state;
-
- mService.updateCpuStats();
-
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next + " (in existing)");
-
- setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
-
- mService.updateLruProcessLocked(next.app, true, null);
- updateLRUListLocked(next);
- mService.updateOomAdjLocked();
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
- if (mStackSupervisor.isFocusedStack(this)) {
-
- // We have special rotation behavior when Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed to
- // get the correct rotation behavior.
- // TODO: Remove this once visibilities are set correctly immediately when starting
- // an activity.
- if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
- 0 /* configChanges */, false /* preserveWindows */);
- }
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null, mDisplayId);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
- false /* deferResume */, mDisplayId);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivityLocked();
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mStackSupervisor.scheduleResumeTopActivities();
- }
- if (!next.visible || next.stopped) {
+ // The contained logic must be synchronized, since we are both changing the visibility
+ // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will
+ // ultimately cause the client code to schedule a layout. Since layouts retrieve the
+ // current {@link Configuration}, we must ensure that the below code updates it before
+ // the layout can occur.
+ synchronized(mWindowManager.getWindowManagerLock()) {
+ // This activity is now becoming visible.
+ if (!next.visible || next.stopped || lastActivityTranslucent) {
next.setVisibility(true);
}
- next.completeResumeLocked();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
- try {
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- next.app.thread.scheduleSendResult(next.appToken, a);
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
+
+ ActivityRecord lastResumedActivity =
+ lastStack == null ? null :lastStack.mResumedActivity;
+ ActivityState lastState = next.state;
+
+ mService.updateCpuStats();
+
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next
+ + " (in existing)");
+
+ setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
+
+ mService.updateLruProcessLocked(next.app, true, null);
+ updateLRUListLocked(next);
+ mService.updateOomAdjLocked();
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
+
+ if (mStackSupervisor.isFocusedStack(this)) {
+
+ // We have special rotation behavior when Keyguard is locked. Make sure all
+ // activity visibilities are set correctly as well as the transition is updated
+ // if needed to get the correct rotation behavior.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
+ 0 /* configChanges */, false /* preserveWindows */);
}
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId),
+ next.mayFreezeScreenLocked(next.app) ? next.appToken : null,
+ mDisplayId);
+ if (config != null) {
+ next.frozenBeforeDestroy = true;
+ }
+ notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next,
+ false /* deferResume */, mDisplayId);
}
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(
- next.newIntents, next.appToken, false /* andPause */);
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivityLocked();
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
+ "Activity config changed during resume: " + next
+ + ", new next: " + nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mStackSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.visible || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
+ try {
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int N = a.size();
+ if (!next.finishing && N > 0) {
+ if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+ "Delivering results to " + next + ": " + a);
+ next.app.thread.scheduleSendResult(next.appToken, a);
+ }
+ }
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
- System.identityHashCode(next), next.getTask().taskId,
- next.shortComponentName);
+ if (next.newIntents != null) {
+ next.app.thread.scheduleNewIntent(
+ next.newIntents, next.appToken, false /* andPause */);
+ }
- next.sleeping = false;
- mService.showUnsupportedZoomDialogIfNeededLocked(next);
- mService.showAskCompatModeDialogLocked(next);
- next.app.pendingUiClean = true;
- next.app.forceProcessStateUpTo(mService.mTopProcessState);
- next.clearOptionsLocked();
- next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
- mService.isNextTransitionForward(), resumeAnimOptions);
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
- if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
- + lastState + ": " + next);
- next.state = lastState;
- if (lastStack != null) {
- lastStack.mResumedActivity = lastResumedActivity;
+ EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
+ System.identityHashCode(next), next.getTask().taskId,
+ next.shortComponentName);
+
+ next.sleeping = false;
+ mService.showUnsupportedZoomDialogIfNeededLocked(next);
+ mService.showAskCompatModeDialogLocked(next);
+ next.app.pendingUiClean = true;
+ next.app.forceProcessStateUpTo(mService.mTopProcessState);
+ next.clearOptionsLocked();
+ next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
+ mService.isNextTransitionForward(), resumeAnimOptions);
+
+ if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ + next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to "
+ + lastState + ": " + next);
+ next.state = lastState;
+ if (lastStack != null) {
+ lastStack.mResumedActivity = lastResumedActivity;
+ }
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
+ mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
+ next.showStartingWindow(null /* prev */, false /* newTask */,
+ false /* taskSwitch */);
+ }
+ mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+ return true;
}
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
- mStackSupervisor.isFrontStackOnDisplay(lastStack)) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwitch */);
- }
- mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
}
// From this point on, if something goes wrong there is no way
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 11d0470..3c1f2d4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -461,6 +461,8 @@
// Forced device usage for communications
private int mForcedUseForComm;
+ private int mForcedUseForCommExt; // External state returned by getters: always consistent
+ // with requests by setters
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
@@ -2890,13 +2892,14 @@
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
+ mForcedUseForCommExt = mForcedUseForComm;
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
}
/** @see AudioManager#isSpeakerphoneOn() */
public boolean isSpeakerphoneOn() {
- return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
+ return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
/** @see AudioManager#setBluetoothScoOn(boolean) */
@@ -2904,6 +2907,13 @@
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
}
+
+ // Only enable calls from system components
+ if (Binder.getCallingUid() >= FIRST_APPLICATION_UID) {
+ mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
+ return;
+ }
+
// for logging only
final String eventSource = new StringBuilder("setBluetoothScoOn(").append(on)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
@@ -2913,11 +2923,21 @@
public void setBluetoothScoOnInt(boolean on, String eventSource) {
if (on) {
+ // do not accept SCO ON if SCO audio is not connected
+ synchronized(mScoClients) {
+ if ((mBluetoothHeadset != null) &&
+ (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
+ != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
+ mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
+ return;
+ }
+ }
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
-
+ mForcedUseForCommExt = mForcedUseForComm;
+ AudioSystem.setParameters("BT_SCO="+ (on ? "on" : "off"));
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0);
sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
@@ -2926,7 +2946,7 @@
/** @see AudioManager#isBluetoothScoOn() */
public boolean isBluetoothScoOn() {
- return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
+ return (mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO);
}
/** @see AudioManager#setBluetoothA2dpOn(boolean) */
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 89e1050..0b11479 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -462,18 +462,25 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- if (useSuggested) {
- if (AudioSystem.isStreamActive(stream, 0)) {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
- flags, packageName, uid);
+ try {
+ if (useSuggested) {
+ if (AudioSystem.isStreamActive(stream, 0)) {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
+ direction, flags, packageName, uid);
+ } else {
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+ AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
+ flags | previousFlagPlaySound, packageName, uid);
+ }
} else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
- AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
- flags | previousFlagPlaySound, packageName, uid);
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
}
- } else {
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- packageName, uid);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
+ + stream + ", flags=" + flags + ", packageName=" + packageName
+ + ", uid=" + uid + ", useSuggested=" + useSuggested
+ + ", previousFlagPlaySound=" + previousFlagPlaySound, e);
}
}
});
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b77ed91..b9a2d18 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1363,6 +1363,10 @@
flags, packageName, TAG);
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting default volume.", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot adjust volume: direction=" + direction
+ + ", suggestedStream=" + suggestedStream + ", flags=" + flags,
+ e);
}
}
});
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 08b96d1..c6f2e8a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1278,13 +1278,11 @@
R.array.config_notificationFallbackVibePattern,
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
-
mInCallNotificationUri = Uri.parse("file://" +
resources.getString(R.string.config_inCallNotificationSound));
mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
- .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
.build();
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
@@ -4019,19 +4017,19 @@
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
-
long[] vibration = record.getVibration();
// Demote sound to vibration if vibration missing & phone in vibration mode.
if (vibration == null
&& hasValidSound
&& (mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_VIBRATE)) {
+ == AudioManager.RINGER_MODE_VIBRATE)
+ && mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
vibration = mFallbackVibrationPattern;
}
hasValidVibrate = vibration != null;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
-
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
@@ -4128,8 +4126,9 @@
boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
// do not play notifications if there is a user of exclusive audio focus
// or the device is in vibrate mode
- if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
- != AudioManager.RINGER_MODE_VIBRATE) {
+ if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
+ != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
@@ -5839,8 +5838,8 @@
private class ShellCmd extends ShellCommand {
public static final String USAGE = "help\n"
- + "allow_listener COMPONENT\n"
- + "disallow_listener COMPONENT\n"
+ + "allow_listener COMPONENT [user_id]\n"
+ + "disallow_listener COMPONENT [user_id]\n"
+ "set_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
@@ -5871,7 +5870,13 @@
pw.println("Invalid listener - must be a ComponentName");
return -1;
}
- getBinderService().setNotificationListenerAccessGranted(cn, true);
+ String userId = getNextArg();
+ if (userId == null) {
+ getBinderService().setNotificationListenerAccessGranted(cn, true);
+ } else {
+ getBinderService().setNotificationListenerAccessGrantedForUser(
+ cn, Integer.parseInt(userId), true);
+ }
}
break;
case "disallow_listener": {
@@ -5880,7 +5885,13 @@
pw.println("Invalid listener - must be a ComponentName");
return -1;
}
- getBinderService().setNotificationListenerAccessGranted(cn, false);
+ String userId = getNextArg();
+ if (userId == null) {
+ getBinderService().setNotificationListenerAccessGranted(cn, false);
+ } else {
+ getBinderService().setNotificationListenerAccessGrantedForUser(
+ cn, Integer.parseInt(userId), false);
+ }
}
break;
case "allow_assistant": {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4fafe34..e254196 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -425,7 +425,7 @@
}
if (useInfo.isUsedByOtherApps(path)) {
- pw.println("used be other apps: " + useInfo.getLoadingPackages(path));
+ pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
}
Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
@@ -441,7 +441,7 @@
// TODO(calin): get the status of the oat file (needs installd call)
pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
if (dexUseInfo.isUsedByOtherApps()) {
- pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
+ pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
}
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ea1c029..224791b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3312,6 +3312,24 @@
removeCodePathLI(dstCodePath);
return null;
}
+
+ // If we have a profile for a compressed APK, copy it to the reference location.
+ // Since the package is the stub one, remove the stub suffix to get the normal package and
+ // APK name.
+ File profileFile = new File(getPrebuildProfilePath(pkg).replace(STUB_SUFFIX, ""));
+ if (profileFile.exists()) {
+ try {
+ // We could also do this lazily before calling dexopt in
+ // PackageDexOptimizer to prevent this happening on first boot. The issue
+ // is that we don't have a good way to say "do this only once".
+ if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
+ pkg.applicationInfo.uid, pkg.packageName)) {
+ Log.e(TAG, "decompressPackage failed to copy system profile!");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e);
+ }
+ }
return dstCodePath;
}
@@ -9723,7 +9741,7 @@
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
- String compilerFilter, boolean bootComplete) {
+ final String compilerFilter, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
@@ -9734,6 +9752,8 @@
for (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
+ boolean useProfileForDexopt = false;
+
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
@@ -9747,11 +9767,30 @@
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.applicationInfo.uid, pkg.packageName)) {
Log.e(TAG, "Installer failed to copy system profile!");
+ } else {
+ // Disabled as this causes speed-profile compilation during first boot
+ // even if things are already compiled.
+ // useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
e);
}
+ } else {
+ PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+ // Handle compressed APKs in this path. Only do this for stubs with profiles to
+ // minimize the number off apps being speed-profile compiled during first boot.
+ // The other paths will not change the filter.
+ if (disabledPs != null && disabledPs.pkg.isStub) {
+ // The package is the stub one, remove the stub suffix to get the normal
+ // package and APK names.
+ String systemProfilePath =
+ getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
+ File systemProfile = new File(systemProfilePath);
+ // Use the profile for compilation if there exists one for the same package
+ // in the system partition.
+ useProfileForDexopt = systemProfile.exists();
+ }
}
}
@@ -9780,17 +9819,13 @@
}
}
- // If the OTA updates a system app which was previously preopted to a non-preopted state
- // the app might end up being verified at runtime. That's because by default the apps
- // are verify-profile but for preopted apps there's no profile.
- // Do a hacky check to ensure that if we have no profiles (a reasonable indication
- // that before the OTA the app was preopted) the app gets compiled with a non-profile
- // filter (by default 'quicken').
- // Note that at this stage unused apps are already filtered.
- if (isSystemApp(pkg) &&
- DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
- !Environment.getReferenceProfile(pkg.packageName).exists()) {
- compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
+ String pkgCompilerFilter = compilerFilter;
+ if (useProfileForDexopt) {
+ // Use background dexopt mode to try and use the profile. Note that this does not
+ // guarantee usage of the profile.
+ pkgCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_BACKGROUND_DEXOPT);
}
// checkProfiles is false to avoid merging profiles during boot which
@@ -9801,7 +9836,7 @@
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
- compilerFilter,
+ pkgCompilerFilter,
dexoptFlags));
switch (primaryDexOptStaus) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f9d10ad..c8cbcd8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2027,10 +2027,14 @@
Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
+ " newVis=" + viewVisibility, stack);
}
- if (viewVisibility == View.VISIBLE &&
- (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || !win.mAppToken.isClientHidden())) {
+ // We should only relayout if the view is visible, it is a starting window, or the
+ // associated appToken is not hidden.
+ final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
+ (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
+ || !win.mAppToken.isClientHidden());
+
+ if (shouldRelayout) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
// We are about to create a surface, but we didn't run a layout yet. So better run
@@ -2191,7 +2195,7 @@
// to the client erroneously accepting a configuration that would have otherwise caused
// an activity restart. We instead hand back the last reported
// {@link MergedConfiguration}.
- if (win.mAppToken == null || !win.mAppToken.isClientHidden()) {
+ if (shouldRelayout) {
win.getMergedConfiguration(mergedConfiguration);
} else {
win.getLastReportedMergedConfiguration(mergedConfiguration);
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 529ac3a..9fa1d68 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -558,6 +558,7 @@
// the phone is quiet
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
mService.buzzBeepBlinkLocked(r);
@@ -568,6 +569,22 @@
}
@Test
+ public void testNoDemoteSoundToVibrateIfNonNotificationStream() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ assertTrue(r.getSound() != null);
+ assertNull(r.getVibration());
+
+ // the phone is quiet
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(1);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverVibrate();
+ verifyBeepLooped();
+ }
+
+ @Test
public void testDemoteSoundToVibrate() throws Exception {
NotificationRecord r = getBeepyNotification();
assertTrue(r.getSound() != null);
@@ -575,6 +592,7 @@
// the phone is quiet
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
mService.buzzBeepBlinkLocked(r);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 8816d43..6674f20 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -19,6 +19,8 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -781,6 +783,13 @@
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
}
+ @Override
+ public boolean hasService(String name) {
+ // Currenty, the only relevant service that ConnectivityService checks for is
+ // ETHERNET_SERVICE.
+ return Context.ETHERNET_SERVICE.equals(name);
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -928,6 +937,13 @@
// will fail. Failing here is much easier to debug.
assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
+ assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS));
+ assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA));
+
+ // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our
+ // mocks, this assert exercises the ConnectivityService code path that ensures that
+ // TYPE_ETHERNET is supported if the ethernet service is running.
+ assertTrue(mCm.isNetworkSupported(TYPE_ETHERNET));
}
@SmallTest