Merge "Import translations. DO NOT MERGE" into oc-dev
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 3221c5d..620e5cf 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -226,11 +226,7 @@
@Override
public void doAlarm(IAlarmCompleteListener alarmManager) {
mCompletion = alarmManager;
- mHandler.post(this);
- }
- @Override
- public void run() {
// Remove this listener from the wrapper cache first; the server side
// already considers it gone
synchronized (AlarmManager.class) {
@@ -239,6 +235,11 @@
}
}
+ mHandler.post(this);
+ }
+
+ @Override
+ public void run() {
// Now deliver it to the app
try {
mListener.onAlarm();
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index f369955..4994fbb 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -39,8 +39,11 @@
* Called whenever IActivityManager.startActivity is called on an activity that is already
* running in the pinned stack and the activity is not actually started, but the task is either
* brought to the front or a new Intent is delivered to it.
+ *
+ * @param clearedTask whether or not the launch activity also cleared the task as a part of
+ * starting
*/
- void onPinnedActivityRestartAttempt();
+ void onPinnedActivityRestartAttempt(boolean clearedTask);
/**
* Called whenever the pinned stack is starting animating a resize.
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 0265ea5..10d6a7b 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -671,22 +671,22 @@
}
/**
- * Make this service run in the foreground, supplying the ongoing
+ * If your service is started (running through {@link Context#startService(Intent)}), then
+ * also make this service run in the foreground, supplying the ongoing
* notification to be shown to the user while in this state.
- * By default services are background, meaning that if the system needs to
- * kill them to reclaim more memory (such as to display a large page in a
- * web browser), they can be killed without too much harm. You can set this
- * flag if killing your service would be disruptive to the user, such as
+ * By default started services are background, meaning that their process won't be given
+ * foreground CPU scheduling (unless something else in that process is foreground) and,
+ * if the system needs to kill them to reclaim more memory (such as to display a large page in a
+ * web browser), they can be killed without too much harm. You use
+ * {@link #startForeground} if killing your service would be disruptive to the user, such as
* if your service is performing background music playback, so the user
* would notice if their music stopped playing.
- *
- * <p>If you need your application to run on platform versions prior to API
- * level 5, you can use the following model to call the the older setForeground()
- * or this modern method as appropriate:
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
- * foreground_compatibility}
- *
+ *
+ * <p>Note that calling this method does <em>not</em> put the service in the started state
+ * itself, even though the name sounds like it. You must always call
+ * {@link #startService(Intent)} first to tell the system it should keep the service running,
+ * and then use this method to tell it to keep it running harder.</p>
+ *
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
* NotificationManager.notify(int, Notification)}; must not be 0.
@@ -716,7 +716,9 @@
/**
* Remove this service from foreground state, allowing it to be killed if
- * more memory is needed.
+ * more memory is needed. This does not stop the service from running (for that
+ * you use {@link #stopSelf()} or related methods), just takes it out of the
+ * foreground state.
*
* @param flags additional behavior options.
* @see #startForeground(int, Notification)
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 307fc91..2df011f 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -39,7 +39,7 @@
}
@Override
- public void onPinnedActivityRestartAttempt() throws RemoteException {
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException {
}
@Override
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d60d4db..64c0f31 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1024,28 +1024,6 @@
}
/**
- * enable or disable Bluetooth HCI snoop log.
- *
- * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission
- *
- * @return true to indicate configure HCI log successfully, or false on
- * immediate error
- * @hide
- */
- public boolean configHciSnoopLog(boolean enable) {
- try {
- mServiceLock.readLock().lock();
- if (mService != null) return mService.configHciSnoopLog(enable);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
- return false;
- }
-
- /**
* Factory reset bluetooth settings.
*
* <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index b337817..43c5ae4 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -97,7 +97,6 @@
ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag);
ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag);
- boolean configHciSnoopLog(boolean enable);
boolean factoryReset();
boolean isMultiAdvertisementSupported();
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index ee56a18..4e53914 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -44,7 +44,6 @@
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
- public boolean blockUninstall;
public boolean instantApp;
public int enabled;
public String lastDisableAppCaller;
@@ -75,7 +74,6 @@
notLaunched = o.notLaunched;
hidden = o.hidden;
suspended = o.suspended;
- blockUninstall = o.blockUninstall;
instantApp = o.instantApp;
enabled = o.enabled;
lastDisableAppCaller = o.lastDisableAppCaller;
@@ -193,9 +191,6 @@
if (suspended != oldState.suspended) {
return false;
}
- if (blockUninstall != oldState.blockUninstall) {
- return false;
- }
if (instantApp != oldState.instantApp) {
return false;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7ead659..0840549 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5176,13 +5176,6 @@
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
- * bluetooth HCI snoop log configuration
- * @hide
- */
- public static final String BLUETOOTH_HCI_LOG =
- "bluetooth_hci_log";
-
- /**
* @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
@Deprecated
@@ -10414,6 +10407,15 @@
public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
/**
+ * Displays toasts when an app posts a notification that does not specify a valid channel.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS =
+ "show_notification_channel_warnings";
+
+ /**
* Whether cell is enabled/disabled
* @hide
*/
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ad3a99d..b0d6395 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1404,6 +1404,11 @@
// or double-clicks that could "dismiss" the floating toolbar.
int delay = ViewConfiguration.getDoubleTapTimeout();
mTextView.postDelayed(mShowFloatingToolbar, delay);
+
+ // This classifies the text and most likely returns before the toolbar is actually
+ // shown. If not, it will update the toolbar with the result when classification
+ // returns. We would rather not wait for a long running classification process.
+ invalidateActionModeAsync();
}
}
@@ -1853,7 +1858,7 @@
mInsertionPointCursorController.invalidateHandle();
}
if (mTextActionMode != null) {
- invalidateActionModeAsync();
+ invalidateActionMode();
}
}
@@ -1945,12 +1950,12 @@
if (mRestartActionModeOnNextRefresh) {
// To avoid distraction, newly start action mode only when selection action
// mode is being restarted.
- startSelectionActionMode();
+ startSelectionActionModeAsync(false);
}
} else if (selectionController == null || !selectionController.isActive()) {
// Insertion action mode is active. Avoid dismissing the selection.
stopTextActionModeWithPreservingSelection();
- startSelectionActionMode();
+ startSelectionActionModeAsync(false);
} else {
mTextActionMode.invalidateContentRect();
}
@@ -2004,15 +2009,8 @@
/**
* Asynchronously starts a selection action mode using the TextClassifier.
*/
- void startSelectionActionModeAsync() {
- getSelectionActionModeHelper().startActionModeAsync();
- }
-
- /**
- * Synchronously starts a selection action mode without the TextClassifier.
- */
- void startSelectionActionMode() {
- getSelectionActionModeHelper().startActionMode();
+ void startSelectionActionModeAsync(boolean adjustSelection) {
+ getSelectionActionModeHelper().startActionModeAsync(adjustSelection);
}
/**
@@ -2022,6 +2020,15 @@
getSelectionActionModeHelper().invalidateActionModeAsync();
}
+ /**
+ * Synchronously invalidates an action mode without the TextClassifier.
+ */
+ private void invalidateActionMode() {
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidate();
+ }
+ }
+
private SelectionActionModeHelper getSelectionActionModeHelper() {
if (mSelectionActionModeHelper == null) {
mSelectionActionModeHelper = new SelectionActionModeHelper(this);
@@ -2075,7 +2082,7 @@
}
if (mTextActionMode != null) {
// Text action mode is already started
- invalidateActionModeAsync();
+ invalidateActionMode();
return false;
}
@@ -4703,7 +4710,7 @@
}
positionAtCursorOffset(offset, false);
if (mTextActionMode != null) {
- invalidateActionModeAsync();
+ invalidateActionMode();
}
}
@@ -4787,7 +4794,7 @@
}
updateDrawable();
if (mTextActionMode != null) {
- invalidateActionModeAsync();
+ invalidateActionMode();
}
}
@@ -5414,13 +5421,8 @@
resetDragAcceleratorState();
if (mTextView.hasSelection()) {
- // Do not invoke the text assistant if this was a drag selection.
- if (mHaventMovedEnoughToStartDrag) {
- startSelectionActionModeAsync();
- } else {
- startSelectionActionMode();
- }
-
+ // Drag selection should not be adjusted by the text classifier.
+ startSelectionActionModeAsync(mHaventMovedEnoughToStartDrag);
}
break;
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index c9d172f..16a1087 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -65,7 +65,7 @@
textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
}
- public void startActionModeAsync() {
+ public void startActionModeAsync(boolean adjustSelection) {
cancelAsyncTask();
if (isNoOpTextClassifier() || !hasSelection()) {
// No need to make an async call for a no-op TextClassifier.
@@ -74,16 +74,16 @@
} else {
resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
- mEditor.getTextView(), TIMEOUT_DURATION,
- mTextClassificationHelper::suggestSelection, this::startActionMode)
+ mEditor.getTextView(),
+ TIMEOUT_DURATION,
+ adjustSelection
+ ? mTextClassificationHelper::suggestSelection
+ : mTextClassificationHelper::classifyText,
+ this::startActionMode)
.execute();
}
}
- public void startActionMode() {
- startActionMode(null);
- }
-
public void invalidateActionModeAsync() {
cancelAsyncTask();
if (isNoOpTextClassifier() || !hasSelection()) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 242dcf5..eaf1115 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10579,7 +10579,7 @@
Selection.setSelection((Spannable) text, start, end);
// Make sure selection mode is engaged.
if (mEditor != null) {
- mEditor.startSelectionActionMode();
+ mEditor.startSelectionActionModeAsync(false);
}
return true;
}
diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java
index b18f83c..eeb3854 100644
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ b/core/java/com/android/internal/inputmethod/LocaleUtils.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.icu.util.ULocale;
import android.os.LocaleList;
+import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -41,7 +42,7 @@
/**
* Calculates a matching score for the single desired locale.
*
- * @see LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])
+ * @see LocaleUtils#filterByLanguage(List, LocaleExtractor, LocaleList, ArrayList)
*
* @param supported The locale supported by IME subtype.
* @param desired The locale preferred by user.
@@ -72,48 +73,6 @@
return 3;
}
- /**
- * Calculates a matching score for the desired locale list.
- *
- * <p>The supported locale gets a matching score of 3 if all language, script and country of the
- * supported locale matches with the desired locale. The supported locale gets a matching
- * score of 2 if the language and script of the supported locale matches with the desired
- * locale. The supported locale gets a matching score of 1 if only language of the supported
- * locale matches with the desired locale. The supported locale gets a matching score of 0 if
- * the language of the supported locale doesn't match with the desired locale.</p>
- *
- * @param supported The locale supported by IME subtyle.
- * @param desired The locale list preferred by user. Typically system locale list.
- * @param out The output buffer to be stored the individual score for the desired language list.
- * The length of {@code out} must be same as the length of {@code desired} language list.
- * @return {@code false} if supported locale doesn't match with any desired locale list.
- * Otherwise {@code true}.
- */
- private static boolean calculateMatchingScore(@NonNull final ULocale supported,
- @NonNull final LocaleList desired, @NonNull byte[] out) {
- if (desired.isEmpty()) {
- return false;
- }
-
- boolean allZeros = true;
- final int N = desired.size();
- for (int i = 0; i < N; ++i) {
- final Locale locale = desired.get(i);
-
- if (!locale.getLanguage().equals(supported.getLanguage())) {
- // TODO: cache the result of addLikelySubtags if it is slow.
- out[i] = 0;
- } else {
- out[i] = calculateMatchingSubScore(
- supported, ULocale.addLikelySubtags(ULocale.forLocale(locale)));
- if (allZeros && out[i] != 0) {
- allZeros = false;
- }
- }
- }
- return !allZeros;
- }
-
private static final class ScoreEntry implements Comparable<ScoreEntry> {
public int mIndex = -1;
@NonNull public final byte[] mScore; // matching score of the i-th system languages.
@@ -175,17 +134,17 @@
/**
* Filters the given items based on language preferences.
*
- * <p>For each language found in {@code preferredLanguages}, this method tries to copy at most
+ * <p>For each language found in {@code preferredLocales}, this method tries to copy at most
* one best-match item from {@code source} to {@code dest}. For example, if
- * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLanguages},
+ * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLocales},
* this method tries to copy at most one English locale, at most one Japanese, and at most one
* French locale from {@code source} to {@code dest}. Here the best matching English locale
* will be searched from {@code source} based on matching score. For the score design, see
- * {@link LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])}</p>
+ * {@link LocaleUtils#calculateMatchingSubScore(ULocale, ULocale)}</p>
*
* @param sources Source items to be filtered.
* @param extractor Type converter from the source items to {@link Locale} object.
- * @param preferredLanguages Ordered list of locales with which the input items will be
+ * @param preferredLocales Ordered list of locales with which the input items will be
* filtered.
* @param dest Destination into which the filtered items will be added.
* @param <T> Type of the data items.
@@ -194,17 +153,43 @@
public static <T> void filterByLanguage(
@NonNull List<T> sources,
@NonNull LocaleExtractor<T> extractor,
- @NonNull LocaleList preferredLanguages,
+ @NonNull LocaleList preferredLocales,
@NonNull ArrayList<T> dest) {
+ if (preferredLocales.isEmpty()) {
+ return;
+ }
+
+ final int numPreferredLocales = preferredLocales.size();
final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
- final byte[] score = new byte[preferredLanguages.size()];
+ final byte[] score = new byte[numPreferredLocales];
+ final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
final int sourceSize = sources.size();
for (int i = 0; i < sourceSize; ++i) {
final Locale locale = extractor.get(sources.get(i));
- if (locale == null ||
- !calculateMatchingScore(ULocale.addLikelySubtags(ULocale.forLocale(locale)),
- preferredLanguages, score)) {
+ if (locale == null) {
+ continue;
+ }
+
+ boolean canSkip = true;
+ for (int j = 0; j < numPreferredLocales; ++j) {
+ final Locale preferredLocale = preferredLocales.get(j);
+ if (!TextUtils.equals(locale.getLanguage(), preferredLocale.getLanguage())) {
+ score[j] = 0;
+ continue;
+ }
+ if (preferredULocaleCache[j] == null) {
+ preferredULocaleCache[j] = ULocale.addLikelySubtags(
+ ULocale.forLocale(preferredLocale));
+ }
+ score[j] = calculateMatchingSubScore(
+ preferredULocaleCache[j],
+ ULocale.addLikelySubtags(ULocale.forLocale(locale)));
+ if (canSkip && score[j] != 0) {
+ canSkip = false;
+ }
+ }
+ if (canSkip) {
continue;
}
diff --git a/core/res/res/values-mcc204/config.xml b/core/res/res/values-mcc204/config.xml
new file mode 100644
index 0000000..790f768
--- /dev/null
+++ b/core/res/res/values-mcc204/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <bool name="config_use_sim_language_file">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
index 63431a4..3049488 100755
--- a/core/res/res/values-mcc310-mnc004/config.xml
+++ b/core/res/res/values-mcc310-mnc004/config.xml
@@ -36,4 +36,7 @@
<bool name="config_auto_attach_data_on_creation">false</bool>
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
+
+ <bool name="config_use_sim_language_file">true</bool>
+
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index a210f5b..6f85081 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -62,4 +62,6 @@
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
+ <bool name="config_use_sim_language_file">true</bool>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8006f78..aeb564b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1299,9 +1299,9 @@
* Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action
protected by the BIND_NETWORK_RECOMMENDATION_SERVICE permission.
- This must be set to a valid network recommendation app.
+ This must be set to a valid network recommendation app or empty.
-->
- <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false">com.android.networkrecommendation</string>
+ <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
<!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
replaced by an app at run-time. When disabled, only the
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e633d66..27f98b3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -644,8 +644,10 @@
<!-- Text shown when viewing channel settings for notifications related to a usb connection -->
<string name="notification_channel_usb">USB connection</string>
- <!-- Text shown when viewing channel settings for notifications related to running foreground
- services [CHAR LIMIT=NONE] -->
+ <!-- This is the label for the notification channel settings that controls the behavior
+ of the notification about applications that are running in the background (that is,
+ perhaps confusingly, running foreground services but not the foreground UI on the screen).
+ [CHAR LIMIT=NONE] -->
<string name="notification_channel_foreground_service">Apps running in background</string>
<!-- Label for foreground service notification when one app is running. [CHAR LIMIT=NONE] -->
@@ -663,7 +665,10 @@
data usage</string>
<!-- Separator for foreground service notification content listing all apps when there
- are multiple apps running [CHAR LIMIT=NONE] -->
+ are multiple apps running. The left and right side may both already be compound
+ (constructed using this separator). Should be kept as short as possible, this is
+ for summary text in the notification where there is not a lot of space.
+ [CHAR LIMIT=NONE] -->
<string name="foreground_service_multiple_separator"><xliff:g id="left_side">%1$s</xliff:g>,
<xliff:g id="right_side">%2$s</xliff:g></string>
@@ -2696,10 +2701,10 @@
<string name="dial">Phone</string>
<!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] -->
- <string name="map">Map</string>
+ <string name="map">Maps</string>
<!-- Label for item in the text selection menu to trigger a Browser app [CHAR LIMIT=20] -->
- <string name="browse">Browse</string>
+ <string name="browse">Browser</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 2ae2ca0..690b051 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -511,6 +511,8 @@
<style name="Widget.CheckedTextView">
<item name="textAlignment">viewStart</item>
+ <item name="breakStrategy">high_quality</item>
+ <item name="hyphenationFrequency">normal</item>
</style>
<style name="Widget.TextView.ListSeparator">
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
index 411a3f8..37b2a50 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
@@ -72,8 +72,6 @@
getAddress();
} else if ("getBondedDevices".equals(command)) {
getBondedDevices();
- } else if ("enableBtSnoop".equals(command)) {
- enableBtSnoop();
} else {
finish(null);
}
@@ -116,12 +114,6 @@
finish(mSuccessResult);
}
- public void enableBtSnoop() {
- Assert.assertTrue("failed to enable snoop log",
- getBluetoothAdapter().configHciSnoopLog(true));
- finish(mSuccessResult);
- }
-
public void finish(Bundle result) {
if (result == null) {
result = new Bundle();
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index aeed20e..a977072 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -397,6 +397,7 @@
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
@@ -404,7 +405,6 @@
Settings.Secure.BACKUP_ENABLED,
Settings.Secure.BACKUP_PROVISIONED,
Settings.Secure.BACKUP_TRANSPORT,
- Settings.Secure.BLUETOOTH_HCI_LOG,
Settings.Secure.CARRIER_APPS_HANDLED,
Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
Settings.Secure.COMPLETED_CATEGORY_PREFIX,
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 5a178a5..eb513e1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -998,7 +998,8 @@
if (mRssi != info.getRssi()) {
mRssi = info.getRssi();
updated = true;
- } else if (mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
+ } else if (mNetworkInfo != null && networkInfo != null
+ && mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
updated = true;
}
mInfo = info;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index f519a90..d4ce40c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -465,9 +465,9 @@
private void updateScoresAndWaitForAccessPointsChangedCallback() throws InterruptedException {
// Updating scores can happen together or one after the other, so the latch countdown is set
// to 2.
- mAccessPointsChangedLatch = new CountDownLatch(3);
+ mAccessPointsChangedLatch = new CountDownLatch(2);
updateScores();
- assertTrue("onAccessPointChanged was not called three times",
+ assertTrue("onAccessPointChanged was not called twice",
mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c7c2222..f475361 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -985,9 +985,6 @@
Settings.Secure.AUTOFILL_SERVICE,
SecureSettingsProto.AUTOFILL_SERVICE);
dumpSetting(s, p,
- Settings.Secure.BLUETOOTH_HCI_LOG,
- SecureSettingsProto.BLUETOOTH_HCI_LOG);
- dumpSetting(s, p,
Settings.Secure.USER_SETUP_COMPLETE,
SecureSettingsProto.USER_SETUP_COMPLETE);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bf70c5a..d15fcae 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2033,7 +2033,9 @@
<!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
<string name="running_foreground_services_title">Apps running in background</string>
- <!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
+ <!-- Descriptive text of an item in the "running foreground services" dialog, telling the
+ user what will happen when they tap on that item (which is an application that has
+ been identified for them as running). [CHAR LIMIT=NONE] -->
<string name="running_foreground_services_msg">Tap for details on battery and data usage</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
index 49e780c..9d286cf 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
@@ -66,7 +66,7 @@
private DialogInterface.OnClickListener mAppClickListener =
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- String pkg = mPackages[which];
+ String pkg = mAdapter.getItem(which).packageName;
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", pkg, null));
startActivity(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index df03fdc..bdc0871 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -107,12 +107,12 @@
}
@Override
- public void onPinnedActivityRestartAttempt() {
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) {
if (!checkCurrentUserId(false /* debug */)) {
return;
}
- mTouchHandler.getMotionHelper().expandPip();
+ mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index fc52a2e..5121c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -140,14 +140,25 @@
* Resizes the pinned stack back to fullscreen.
*/
void expandPip() {
+ expandPip(false /* skipAnimation */);
+ }
+
+ /**
+ * Resizes the pinned stack back to fullscreen.
+ */
+ void expandPip(boolean skipAnimation) {
cancelAnimations();
mHandler.post(() -> {
try {
- mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
- true /* allowResizeInDockedMode */, true /* preserveWindows */,
- true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+ if (skipAnimation) {
+ mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */);
+ } else {
+ mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
+ true /* allowResizeInDockedMode */, true /* preserveWindows */,
+ true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+ }
} catch (RemoteException e) {
- Log.e(TAG, "Error showing PiP menu activity", e);
+ Log.e(TAG, "Error expanding PiP activity", e);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 657f08b..9735bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -625,7 +625,7 @@
}
@Override
- public void onPinnedActivityRestartAttempt() {
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) {
if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
if (!checkCurrentUserId(DEBUG)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a9e1f61..f431517 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -158,7 +158,7 @@
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
public void onActivityPinned(String packageName) { }
public void onActivityUnpinned() { }
- public void onPinnedActivityRestartAttempt() { }
+ public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
public void onPinnedStackAnimationStarted() { }
public void onPinnedStackAnimationEnded() { }
public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
@@ -223,10 +223,11 @@
}
@Override
- public void onPinnedActivityRestartAttempt()
+ public void onPinnedActivityRestartAttempt(boolean clearedTask)
throws RemoteException{
mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
- mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
@@ -1294,7 +1295,8 @@
}
case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+ mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
+ msg.arg1 != 0);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
index 82910b8..454edbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
@@ -34,7 +34,7 @@
* A utility class to colorize bitmaps with a color gradient and a special blending mode
*/
public class ImageGradientColorizer {
- public Bitmap colorize(Drawable drawable, int backgroundColor) {
+ public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
int size = Math.min(width, height);
@@ -70,8 +70,6 @@
0, 0, 0, 1, 0,
});
- drawable.setColorFilter(new ColorMatrixColorFilter(m));
- drawable.draw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
LinearGradient linearGradient = new LinearGradient(0, 0, size, 0,
new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
@@ -81,16 +79,27 @@
Canvas fadeInCanvas = new Canvas(fadeIn);
drawable.clearColorFilter();
drawable.draw(fadeInCanvas);
+
+ if (isRtl) {
+ // Let's flip the gradient
+ fadeInCanvas.translate(size, 0);
+ fadeInCanvas.scale(-1, 1);
+ }
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
fadeInCanvas.drawPaint(paint);
+
+ Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ coloredPaint.setColorFilter(new ColorMatrixColorFilter(m));
+ coloredPaint.setAlpha((int) (0.5f * 255));
+ canvas.drawBitmap(fadeIn, 0, 0, coloredPaint);
+
+ linearGradient = new LinearGradient(0, 0, size, 0,
+ new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
+ new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
+ paint.setShader(linearGradient);
+ fadeInCanvas.drawPaint(paint);
canvas.drawBitmap(fadeIn, 0, 0, null);
- linearGradient = new LinearGradient(0, 0, size, 0,
- new int[] {backgroundColor, Color.argb(0.5f, tr, tg, tb), 0},
- new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
- paint.setShader(linearGradient);
- paint.setXfermode(null);
- canvas.drawPaint(paint);
return newBitmap;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index cef225b..52c053f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Icon;
import android.support.v4.graphics.ColorUtils;
import android.support.v7.graphics.Palette;
+import android.util.LayoutDirection;
import com.android.systemui.R;
@@ -187,7 +188,9 @@
: R.color.notification_material_background_color;
backgroundColor = mContext.getColor(id);
}
- Bitmap colorized = mColorizer.colorize(drawable, backgroundColor);
+ Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
+ mContext.getResources().getConfiguration().getLayoutDirection() ==
+ LayoutDirection.RTL);
builder.setLargeIcon(Icon.createWithBitmap(colorized));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index d57f813..8934460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -28,17 +28,22 @@
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
+import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
+import org.junit.runner.RunWith;
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class DozeTriggersTest {
private Context mContext;
private DozeTriggers mTriggers;
@@ -46,7 +51,7 @@
private DozeHostFake mHost;
private AmbientDisplayConfiguration mConfig;
private DozeParameters mParameters;
- private SensorManagerFake mSensors;
+ private FakeSensorManager mSensors;
private Handler mHandler;
private WakeLock mWakeLock;
private Instrumentation mInstrumentation;
@@ -65,7 +70,7 @@
mHost = new DozeHostFake();
mConfig = DozeConfigurationUtil.createMockConfig();
mParameters = DozeConfigurationUtil.createMockParameters();
- mSensors = new SensorManagerFake(mContext);
+ mSensors = new FakeSensorManager(mContext);
mHandler = new Handler(Looper.getMainLooper());
mWakeLock = new WakeLockFake();
@@ -76,29 +81,29 @@
}
@Test
- @Ignore("setup crashes on virtual devices")
public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
mInstrumentation.runOnMainSync(()->{
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
- mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE);
+ mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
mHost.callback.onNotificationHeadsUp();
});
mInstrumentation.runOnMainSync(() -> {
- mSensors.PROXIMITY.sendProximityResult(false); /* Near */
+ mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
});
verify(mMachine, never()).requestState(any());
+ verify(mMachine, never()).requestPulse(anyInt());
mInstrumentation.runOnMainSync(()->{
mHost.callback.onNotificationHeadsUp();
});
mInstrumentation.runOnMainSync(() -> {
- mSensors.PROXIMITY.sendProximityResult(true); /* Far */
+ mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
});
verify(mMachine).requestPulse(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
similarity index 60%
rename from packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java
rename to packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
index 5b4b891..30be665 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.doze;
+package com.android.systemui.utils.hardware;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -33,6 +33,8 @@
import com.google.android.collect.Lists;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -44,18 +46,38 @@
* Note that this class ignores the "Handler" argument, so the test is responsible for calling the
* listener on the right thread.
*/
-public class SensorManagerFake extends SensorManager {
+public class FakeSensorManager extends SensorManager {
- public MockSensor PROXIMITY;
+ private final MockProximitySensor mMockProximitySensor;
- public SensorManagerFake(Context context) {
- PROXIMITY = new MockSensor(context.getSystemService(SensorManager.class)
- .getDefaultSensor(Sensor.TYPE_PROXIMITY));
+ public FakeSensorManager(Context context) throws Exception {
+ Sensor proxSensor = context.getSystemService(SensorManager.class)
+ .getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (proxSensor == null) {
+ // No prox? Let's create a fake one!
+ proxSensor = createSensor(Sensor.TYPE_PROXIMITY);
+ }
+ mMockProximitySensor = new MockProximitySensor(proxSensor);
+ }
+
+ public MockProximitySensor getMockProximitySensor() {
+ return mMockProximitySensor;
+ }
+
+ @Override
+ public Sensor getDefaultSensor(int type) {
+ Sensor s = super.getDefaultSensor(type);
+ if (s != null) {
+ return s;
+ }
+ // Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
+ // return non-wakeup sensors if we can't find a wakeup sensor.
+ return getDefaultSensor(type, false /* wakeup */);
}
@Override
protected List<Sensor> getFullSensorList() {
- return Lists.newArrayList(PROXIMITY.sensor);
+ return Lists.newArrayList(mMockProximitySensor.sensor);
}
@Override
@@ -65,8 +87,8 @@
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
- if (sensor == PROXIMITY.sensor || sensor == null) {
- PROXIMITY.listeners.remove(listener);
+ if (sensor == mMockProximitySensor.sensor || sensor == null) {
+ mMockProximitySensor.listeners.remove(listener);
}
}
@@ -74,8 +96,8 @@
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
int delayUs,
Handler handler, int maxReportLatencyUs, int reservedFlags) {
- if (sensor == PROXIMITY.sensor) {
- PROXIMITY.listeners.add(listener);
+ if (sensor == mMockProximitySensor.sensor) {
+ mMockProximitySensor.listeners.add(listener);
return true;
}
return false;
@@ -141,11 +163,44 @@
return false;
}
- public class MockSensor {
+ private Sensor createSensor(int type) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+
+ setSensorType(sensor, type);
+ setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+ setSensorField(sensor, "mVendor", "Mock Vendor");
+ setSensorField(sensor, "mVersion", 1);
+ setSensorField(sensor, "mHandle", -1);
+ setSensorField(sensor, "mMaxRange", 10);
+ setSensorField(sensor, "mResolution", 1);
+ setSensorField(sensor, "mPower", 1);
+ setSensorField(sensor, "mMinDelay", 1000);
+ setSensorField(sensor, "mMaxDelay", 1000000000);
+ setSensorField(sensor, "mFlags", 0);
+ setSensorField(sensor, "mId", -1);
+
+ return sensor;
+ }
+
+ private void setSensorField(Sensor sensor, String fieldName, Object value) throws Exception {
+ Field field = Sensor.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(sensor, value);
+ }
+
+ private void setSensorType(Sensor sensor, int type) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ }
+
+ public class MockProximitySensor {
final Sensor sensor;
final ArraySet<SensorEventListener> listeners = new ArraySet<>();
- private MockSensor(Sensor sensor) {
+ private MockProximitySensor(Sensor sensor) {
this.sensor = sensor;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index a68a7dd..d1a43d2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -774,7 +774,7 @@
}
// High level policy: apps are generally ineligible for backup if certain conditions apply
- public static boolean appIsEligibleForBackup(ApplicationInfo app) {
+ public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
// 1. their manifest states android:allowBackup="false"
if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
@@ -790,15 +790,18 @@
return false;
}
- return true;
+ // Everything else checks out; the only remaining roadblock would be if the
+ // package were disabled
+ return !appIsDisabled(app, pm);
}
- // Checks if the app is in a stopped state, that means it won't receive broadcasts.
+ // Checks if the app is in a stopped state. This is not part of the general "eligible for
+ // backup?" check because we *do* still need to restore data to apps in this state (e.g.
+ // newly-installing ones)
private static boolean appIsStopped(ApplicationInfo app) {
return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
}
- // We also avoid backups of 'disabled' apps
private static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
switch (pm.getApplicationEnabledSetting(app.packageName)) {
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
@@ -1526,7 +1529,8 @@
foundApps.add(pkgName); // all apps that we've addressed already
try {
PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
- if (appGetsFullBackup(pkg) && appIsEligibleForBackup(pkg.applicationInfo)) {
+ if (appGetsFullBackup(pkg)
+ && appIsEligibleForBackup(pkg.applicationInfo, mPackageManager)) {
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
@@ -1545,7 +1549,8 @@
// New apps can arrive "out of band" via OTA and similar, so we also need to
// scan to make sure that we're tracking all full-backup candidates properly
for (PackageInfo app : apps) {
- if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
+ if (appGetsFullBackup(app)
+ && appIsEligibleForBackup(app.applicationInfo, mPackageManager)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "New full backup app " + app.packageName + " found");
@@ -1574,7 +1579,8 @@
changed = true;
schedule = new ArrayList<FullBackupEntry>(apps.size());
for (PackageInfo info : apps) {
- if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) {
+ if (appGetsFullBackup(info)
+ && appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
@@ -2032,7 +2038,8 @@
for (String packageName : pkgList) {
try {
PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
- if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
+ if (appGetsFullBackup(app)
+ && appIsEligibleForBackup(app.applicationInfo, mPackageManager)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
@@ -2432,7 +2439,7 @@
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
- if (!appIsEligibleForBackup(packageInfo.applicationInfo)) {
+ if (!appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager)) {
sendBackupOnPackageResult(observer, packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
continue;
@@ -2952,7 +2959,7 @@
try {
mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
PackageManager.GET_SIGNATURES);
- if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo)) {
+ if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo, mPackageManager)) {
// The manifest has changed but we had a stale backup request pending.
// This won't happen again because the app won't be requesting further
// backups.
@@ -4399,7 +4406,7 @@
Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
while (iter.hasNext()) {
PackageInfo pkg = iter.next().getValue();
- if (!appIsEligibleForBackup(pkg.applicationInfo)
+ if (!appIsEligibleForBackup(pkg.applicationInfo, mPackageManager)
|| appIsStopped(pkg.applicationInfo)) {
iter.remove();
if (DEBUG) {
@@ -4681,7 +4688,7 @@
PackageInfo info = mPackageManager.getPackageInfo(pkg,
PackageManager.GET_SIGNATURES);
mCurrentPackage = info;
- if (!appIsEligibleForBackup(info.applicationInfo)) {
+ if (!appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
// Cull any packages that have indicated that backups are not permitted,
// that run as system-domain uids but do not define their own backup agents,
// as well as any explicit mention of the 'special' shared-storage agent
@@ -8658,7 +8665,7 @@
continue;
}
- if (appIsEligibleForBackup(info.applicationInfo)) {
+ if (appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
@@ -10830,9 +10837,8 @@
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
- if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
- appIsStopped(packageInfo.applicationInfo) ||
- appIsDisabled(packageInfo.applicationInfo, mPackageManager)) {
+ if (!appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager) ||
+ appIsStopped(packageInfo.applicationInfo)) {
return false;
}
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 09f240f..642b8bf 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -140,7 +140,7 @@
int N = pkgs.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = pkgs.get(a);
- if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo)) {
+ if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
pkgs.remove(a);
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 18b4571..c785fb9 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1494,16 +1494,6 @@
if (mGetNameAddressOnly) return;
}
- try {
- boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
- Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
- if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) {
- Slog.e(TAG,"IBluetooth.configHciSnoopLog return false");
- }
- } catch (RemoteException e) {
- Slog.e(TAG,"Unable to call configHciSnoopLog", e);
- }
-
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4313533..a5615a9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -166,8 +166,11 @@
*/
static final class ActiveForegroundApp {
String mPackageName;
+ int mUid;
CharSequence mLabel;
boolean mShownWhileScreenOn;
+ boolean mAppOnTop;
+ boolean mShownWhileTop;
long mStartTime;
long mStartVisibleTime;
long mEndTime;
@@ -728,11 +731,12 @@
synchronized (mAm) {
final long now = SystemClock.elapsedRealtime();
final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
+ long nextUpdateTime = Long.MAX_VALUE;
if (smap != null) {
for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i);
if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) {
- if (aa.mEndTime < (aa.mStartVisibleTime
+ if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime
+ mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
// Check to see if this should still be displayed... we continue
// until it has been shown for at least the timeout duration.
@@ -741,6 +745,12 @@
smap.mActiveForegroundApps.removeAt(i);
smap.mActiveForegroundAppsChanged = true;
continue;
+ } else {
+ long hideTime = aa.mStartVisibleTime
+ + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
+ if (hideTime < nextUpdateTime) {
+ nextUpdateTime = hideTime;
+ }
}
} else {
// This was up for longer than the timeout, so just remove immediately.
@@ -749,10 +759,17 @@
continue;
}
}
- if (active == null) {
- active = new ArrayList<>();
+ if (!aa.mAppOnTop) {
+ if (active == null) {
+ active = new ArrayList<>();
+ }
+ active.add(aa);
}
- active.add(aa);
+ }
+ smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
+ if (nextUpdateTime < Long.MAX_VALUE) {
+ Message msg = smap.obtainMessage();
+ smap.sendMessageAtTime(msg, nextUpdateTime);
}
}
if (!smap.mActiveForegroundAppsChanged) {
@@ -842,7 +859,7 @@
active.mNumActive--;
if (active.mNumActive <= 0) {
active.mEndTime = SystemClock.elapsedRealtime();
- if (active.mEndTime >= (active.mStartVisibleTime
+ if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime
+ mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
// Have been active for long enough that we will remove it immediately.
smap.mActiveForegroundApps.remove(r.packageName);
@@ -887,6 +904,31 @@
}
}
+ void foregroundServiceProcStateChangedLocked(UidRecord uidRec) {
+ ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.uid));
+ if (smap != null) {
+ boolean changed = false;
+ for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
+ ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
+ if (active.mUid == uidRec.uid) {
+ if (uidRec.curProcState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (!active.mAppOnTop) {
+ active.mAppOnTop = true;
+ changed = true;
+ }
+ active.mShownWhileTop = true;
+ } else if (active.mAppOnTop) {
+ active.mAppOnTop = false;
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ requestUpdateActiveForegroundAppsLocked(smap, 0);
+ }
+ }
+ }
+
private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
Notification notification, int flags) {
if (id != 0) {
@@ -948,7 +990,13 @@
if (active == null) {
active = new ActiveForegroundApp();
active.mPackageName = r.packageName;
+ active.mUid = r.appInfo.uid;
active.mShownWhileScreenOn = mScreenOn;
+ if (r.app != null) {
+ active.mAppOnTop = active.mShownWhileTop =
+ r.app.uidRecord.curProcState
+ <= ActivityManager.PROCESS_STATE_TOP;
+ }
active.mStartTime = active.mStartVisibleTime
= SystemClock.elapsedRealtime();
smap.mActiveForegroundApps.put(r.packageName, active);
@@ -2790,6 +2838,9 @@
if (!doit && didSomething) {
return true;
}
+ if (doit && filterByClasses == null) {
+ forceStopPackageLocked(packageName, mServiceMap.valueAt(i).mUserId);
+ }
}
} else {
ServiceMap smap = mServiceMap.get(userId);
@@ -2798,6 +2849,9 @@
didSomething = collectPackageServicesLocked(packageName, filterByClasses,
evenPersistent, doit, killProcess, items);
}
+ if (doit && filterByClasses == null) {
+ forceStopPackageLocked(packageName, userId);
+ }
}
if (mTmpCollectionResults != null) {
@@ -2806,10 +2860,11 @@
}
mTmpCollectionResults.clear();
}
+
return didSomething;
}
- void removeUninstalledPackageLocked(String packageName, int userId) {
+ void forceStopPackageLocked(String packageName, int userId) {
ServiceMap smap = mServiceMap.get(userId);
if (smap != null && smap.mActiveForegroundApps.size() > 0) {
for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
@@ -3640,6 +3695,10 @@
}
pw.print(" mNumActive=");
pw.print(aa.mNumActive);
+ pw.print(" mAppOnTop=");
+ pw.print(aa.mAppOnTop);
+ pw.print(" mShownWhileTop=");
+ pw.print(aa.mShownWhileTop);
pw.print(" mShownWhileScreenOn=");
pw.println(aa.mShownWhileScreenOn);
pw.print(" mStartTime=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4d09313..ba48783 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -120,7 +120,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
@@ -5427,7 +5426,7 @@
public static class DumpStackFileObserver extends FileObserver {
// Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
- static final int TRACE_DUMP_TIMEOUT_SECONDS = TRACE_DUMP_TIMEOUT_MS / 1000;
+ static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
private final String mTracesPath;
private boolean mClosed;
@@ -5443,21 +5442,41 @@
notify();
}
- public void dumpWithTimeout(int pid) {
+ public long dumpWithTimeout(int pid, long timeout) {
sendSignal(pid, SIGNAL_QUIT);
+ final long start = SystemClock.elapsedRealtime();
+
+ final long waitTime = Math.min(timeout, TRACE_DUMP_TIMEOUT_MS);
synchronized (this) {
try {
- wait(TRACE_DUMP_TIMEOUT_MS); // Wait for traces file to be closed.
+ wait(waitTime); // Wait for traces file to be closed.
} catch (InterruptedException e) {
Slog.wtf(TAG, e);
}
}
+
+ // This avoids a corner case of passing a negative time to the native
+ // trace in case we've already hit the overall timeout.
+ final long timeWaited = SystemClock.elapsedRealtime() - start;
+ if (timeWaited >= timeout) {
+ return timeWaited;
+ }
+
if (!mClosed) {
Slog.w(TAG, "Didn't see close of " + mTracesPath + " for pid " + pid +
". Attempting native stack collection.");
- Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath, TRACE_DUMP_TIMEOUT_SECONDS);
+
+ final long nativeDumpTimeoutMs = Math.min(
+ NATIVE_DUMP_TIMEOUT_MS, timeout - timeWaited);
+
+ Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath,
+ (int) (nativeDumpTimeoutMs / 1000));
}
+
+ final long end = SystemClock.elapsedRealtime();
mClosed = false;
+
+ return (end - start);
}
}
@@ -5467,6 +5486,9 @@
// Use a FileObserver to detect when traces finish writing.
// The order of traces is considered important to maintain for legibility.
DumpStackFileObserver observer = new DumpStackFileObserver(tracesPath);
+
+ // We must complete all stack dumps within 20 seconds.
+ long remainingTime = 20 * 1000;
try {
observer.startWatching();
@@ -5476,10 +5498,18 @@
for (int i = 0; i < num; i++) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
+ firstPids.get(i));
- final long sime = SystemClock.elapsedRealtime();
- observer.dumpWithTimeout(firstPids.get(i));
- if (DEBUG_ANR) Slog.d(TAG, "Done with pid " + firstPids.get(i)
- + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
+ final long timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+ }
}
}
@@ -5487,12 +5517,24 @@
if (nativePids != null) {
for (int pid : nativePids) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
- final long sime = SystemClock.elapsedRealtime();
+ final long nativeDumpTimeoutMs = Math.min(
+ DumpStackFileObserver.NATIVE_DUMP_TIMEOUT_MS, remainingTime);
+ final long start = SystemClock.elapsedRealtime();
Debug.dumpNativeBacktraceToFileTimeout(
- pid, tracesPath, DumpStackFileObserver.TRACE_DUMP_TIMEOUT_SECONDS);
- if (DEBUG_ANR) Slog.d(TAG, "Done with native pid " + pid
- + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
+ pid, tracesPath, (int) (nativeDumpTimeoutMs / 1000));
+ final long timeTaken = SystemClock.elapsedRealtime() - start;
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+ }
}
}
@@ -5516,12 +5558,20 @@
ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
if (lastPids.indexOfKey(stats.pid) >= 0) {
numProcs++;
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid "
- + stats.pid);
- final long stime = SystemClock.elapsedRealtime();
- observer.dumpWithTimeout(stats.pid);
- if (DEBUG_ANR) Slog.d(TAG, "Done with extra pid " + stats.pid
- + " in " + (SystemClock.elapsedRealtime()-stime) + "ms");
+
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+
+ final long timeTaken = observer.dumpWithTimeout(stats.pid, remainingTime);
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + stats.pid +
+ "); deadline exceeded.");
+ return;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with extra pid " + stats.pid + " in " + timeTaken + "ms");
+ }
} else if (DEBUG_ANR) {
Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
+ stats.pid);
@@ -19007,7 +19057,7 @@
removeTasksByPackageNameLocked(ssp, userId);
- mServices.removeUninstalledPackageLocked(ssp, userId);
+ mServices.forceStopPackageLocked(ssp, userId);
// Hide the "unsupported display" dialog if necessary.
if (mUnsupportedDisplaySizeDialog != null && ssp.equals(
@@ -19890,8 +19940,9 @@
}
/**
- * NOTE: For the pinned stack, this method is only called after the bounds animation has
- * animated the stack to the fullscreen.
+ * NOTE: For the pinned stack, this method is usually called after the bounds animation has
+ * animated the stack to the fullscreen, but can also be called if we are relaunching an
+ * activity and clearing the task at the same time.
*/
@Override
public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
@@ -22277,6 +22328,9 @@
if (uidRec.curProcState > app.curProcState) {
uidRec.curProcState = app.curProcState;
}
+ if (app.foregroundServices) {
+ uidRec.foregroundServices = true;
+ }
}
}
@@ -22518,6 +22572,9 @@
uidRec.setWhitelist = uidRec.curWhitelist;
enqueueUidChangeLocked(uidRec, -1, uidChange);
noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ if (uidRec.foregroundServices) {
+ mServices.foregroundServiceProcStateChangedLocked(uidRec);
+ }
}
}
if (mLocalPowerManager != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 912b22c..d348224 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1659,21 +1659,13 @@
}
if (mStackId == DOCKED_STACK_ID) {
- final ActivityRecord r = topStack.topRunningActivityLocked();
-
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isAssistantStack()) {
return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
: STACK_INVISIBLE;
}
-
- // Otherwise, the docked stack is always visible, except in the case where the top
- // running activity task in the focus stack doesn't support any form of resizing but we
- // show it for the home task even though it's not resizable.
- final TaskRecord task = r != null ? r.getTask() : null;
- return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE
- : STACK_INVISIBLE;
+ return STACK_VISIBLE;
}
// Set home stack to invisible when it is below but not immediately below the docked stack
@@ -1692,14 +1684,17 @@
mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
stackBehindTopIndex--;
}
- if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID)
- && stackIndex == stackBehindTopIndex) {
- // Stacks directly behind the docked or pinned stack are always visible.
- return STACK_VISIBLE;
- }
-
final int stackBehindTopId = (stackBehindTopIndex >= 0)
? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+ if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID)
+ && (stackIndex == stackBehindTopIndex
+ || (stackBehindTopId == DOCKED_STACK_ID
+ && stackIndex == stackBehindTopIndex - 1))) {
+ // Stacks directly behind the docked or pinned stack are always visible.
+ // Also this stack is visible if behind docked stack and the docked stack is behind the
+ // top-most pinned stack
+ return STACK_VISIBLE;
+ }
if (StackId.isBackdropToTranslucentActivity(topStackId)
&& topStack.isStackTranslucent(starting, stackBehindTopId)) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 86a3103..1f1aa8e 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -575,12 +575,15 @@
return;
}
- if (startedActivityStackId == PINNED_STACK_ID
- && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP)) {
+ boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+ == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+ if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
+ || result == START_DELIVERED_TO_TOP || clearedTask)) {
// The activity was already running in the pinned stack so it wasn't started, but either
// brought to the front or the new intent was delivered to it since it was already in
// front. Notify anyone interested in this piece of information.
- mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt();
+ mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+ clearedTask);
return;
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b025385..fbc2bd2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -103,7 +103,6 @@
int renderThreadTid; // TID for RenderThread
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
- boolean setIsForeground; // Running foreground UI when last set?
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
boolean hasClientActivities; // Are there any client services with activities?
boolean hasStartedServices; // Are there any started services running in this process?
@@ -303,9 +302,8 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
- if (setIsForeground || foregroundServices || forcingToForeground != null) {
- pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);
- pw.print(" foregroundServices="); pw.print(foregroundServices);
+ if (foregroundServices || forcingToForeground != null) {
+ pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
pw.print(" forcingToForeground="); pw.println(forcingToForeground);
}
if (reportedInteraction || fgInteractionTime != 0) {
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 7d2bc5b..f5d7b68 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -104,7 +104,7 @@
};
private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
- l.onPinnedActivityRestartAttempt();
+ l.onPinnedActivityRestartAttempt(m.arg1 != 0);
};
private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
@@ -300,10 +300,11 @@
* running in the pinned stack and the activity was not actually started, but the task is
* either brought to the front or a new Intent is delivered to it.
*/
- void notifyPinnedActivityRestartAttempt() {
+ void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
final Message msg =
- mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+ mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
+ clearedTask ? 1 : 0, 0);
forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index c0fb77f..c411bce 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -35,6 +35,7 @@
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
long lastBackgroundTime;
boolean ephemeral;
+ boolean foregroundServices;
boolean curWhitelist;
boolean setWhitelist;
boolean idle;
@@ -102,6 +103,7 @@
public void reset() {
curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ foregroundServices = false;
}
public void updateHasInternetPermission() {
@@ -131,6 +133,9 @@
if (ephemeral) {
sb.append(" ephemeral");
}
+ if (foregroundServices) {
+ sb.append(" fgServices");
+ }
if (curWhitelist) {
sb.append(" whitelist");
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d35104f..3053879 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -18,12 +18,10 @@
import android.annotation.NonNull;
import android.media.AudioAttributes;
-import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioSystem;
import android.media.IPlaybackConfigDispatcher;
-import android.media.MediaRecorder;
import android.media.PlayerBase;
import android.media.VolumeShaper;
import android.os.Binder;
@@ -99,13 +97,6 @@
apc.init();
synchronized(mPlayerLock) {
mPlayers.put(newPiid, apc);
- if (mDuckedUids.contains(new Integer(apc.getClientUid()))) {
- if (DEBUG) { Log.v(TAG, " > trackPlayer() piid=" + newPiid + " must be ducked"); }
- mDuckedPlayers.add(new Integer(newPiid));
- // FIXME here the player needs to be put in a state that is the same as if it
- // had been ducked as it starts. At the moment, this works already for linked
- // players, as is the case in gapless playback.
- }
}
return newPiid;
}
@@ -131,10 +122,11 @@
final boolean change;
synchronized(mPlayerLock) {
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
- // FIXME SoundPool not ready for state reporting
- if (apc != null
- && apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL)
- {
+ if (apc == null) {
+ return;
+ }
+ if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ // FIXME SoundPool not ready for state reporting
return;
}
if (checkConfigurationCaller(piid, apc, binderUid)) {
@@ -144,12 +136,8 @@
Log.e(TAG, "Error handling event " + event);
change = false;
}
- if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
- && mDuckedUids.contains(new Integer(apc.getClientUid()))) {
- if (DEBUG) { Log.v(TAG, " > playerEvent() piid=" + piid + " must be ducked"); }
- if (!mDuckedPlayers.contains(new Integer(piid))) {
- mDuckedPlayers.add(new Integer(piid));
- }
+ if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ mDuckingManager.checkDuck(apc);
}
}
if (change) {
@@ -163,6 +151,7 @@
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
if (checkConfigurationCaller(piid, apc, binderUid)) {
mPlayers.remove(new Integer(piid));
+ mDuckingManager.removeReleased(apc);
}
}
}
@@ -182,10 +171,8 @@
conf.dump(pw);
}
// ducked players
- pw.println("\n ducked player piids:");
- for (int piid : mDuckedPlayers) {
- pw.println(" " + piid);
- }
+ pw.println("\n ducked players:");
+ mDuckingManager.dump(pw);
// players muted due to the device ringing or being in a call
pw.println("\n muted player piids:");
for (int piid : mMutedPlayers) {
@@ -274,10 +261,9 @@
//=================================================================
// PlayerFocusEnforcer implementation
- private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
- // size of 2 for typical cases of double-ducking, not expected to grow beyond that, but can
- private final ArrayList<Integer> mDuckedUids = new ArrayList<Integer>(2);
+
+ private final DuckingManager mDuckingManager = new DuckingManager();
@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
@@ -286,60 +272,46 @@
winner.getClientUid(), loser.getClientUid()));
}
synchronized (mPlayerLock) {
- final Integer loserUid = new Integer(loser.getClientUid());
- if (!mDuckedUids.contains(loserUid)) {
- mDuckedUids.add(loserUid);
- }
if (mPlayers.isEmpty()) {
return true;
}
- final Set<Integer> piidSet = mPlayers.keySet();
- final Iterator<Integer> piidIterator = piidSet.iterator();
- // find which players to duck
- while (piidIterator.hasNext()) {
- final Integer piid = piidIterator.next();
- final AudioPlaybackConfiguration apc = mPlayers.get(piid);
- if (apc == null) {
- continue;
- }
+ // check if this UID needs to be ducked (return false if not), and gather list of
+ // eligible players to duck
+ final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
+ final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
+ new ArrayList<AudioPlaybackConfiguration>();
+ while (apcIterator.hasNext()) {
+ final AudioPlaybackConfiguration apc = apcIterator.next();
if (!winner.hasSameUid(apc.getClientUid())
&& loser.hasSameUid(apc.getClientUid())
&& apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
{
- if (mDuckedPlayers.contains(new Integer(piid))) {
- if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
- } else if (MediaFocusControl.ENFORCE_DUCKING
+ if (MediaFocusControl.ENFORCE_DUCKING
&& MediaFocusControl.ENFORCE_DUCKING_FOR_NEW
&& loser.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL) {
// legacy behavior, apps used to be notified when they should be ducking
- if (DEBUG) { Log.v(TAG, "not ducking player " + piid + ": old SDK"); }
+ if (DEBUG) {Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
+ + ": old SDK"); }
return false;
} else if (apc.getAudioAttributes().getContentType() ==
AudioAttributes.CONTENT_TYPE_SPEECH) {
// the player is speaking, ducking will make the speech unintelligible
// so let the app handle it instead
- if (DEBUG) { Log.v(TAG, "not ducking player " + piid + ": SPEECH"); }
+ if (DEBUG) { Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
+ + ": SPEECH"); }
return false;
} else if (apc.getPlayerType()
== AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
// TODO support ducking of SoundPool players
return false;
- } else {
- try {
- Log.v(TAG, "ducking player " + piid);
- apc.getPlayerProxy().applyVolumeShaper(
- DUCK_VSHAPE,
- PLAY_CREATE_IF_NEEDED);
- mDuckedPlayers.add(new Integer(piid));
- } catch (Exception e) {
- Log.e(TAG, "Error ducking player " + piid, e);
- // something went wrong trying to duck, so let the app handle it
- // instead, it may know things we don't
- return false;
- }
}
+ apcsToDuck.add(apc);
}
}
+ // add the players eligible for ducking to the list, and duck them
+ // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
+ // players of the same uid start, they will be ducked by DuckingManager.checkDuck())
+ mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
}
return true;
}
@@ -348,37 +320,7 @@
public void unduckPlayers(FocusRequester winner) {
if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
synchronized (mPlayerLock) {
- if (mDuckedPlayers.isEmpty()) {
- mDuckedUids.remove(new Integer(winner.getClientUid()));
- return;
- }
- final ArrayList<Integer> playersToRemove =
- new ArrayList<Integer>(mDuckedPlayers.size());
- for (int piid : mDuckedPlayers) {
- final AudioPlaybackConfiguration apc = mPlayers.get(piid);
- if (apc != null) {
- if (winner.hasSameUid(apc.getClientUid())) {
- try {
- Log.v(TAG, "unducking player " + piid);
- apc.getPlayerProxy().applyVolumeShaper(
- DUCK_ID,
- VolumeShaper.Operation.REVERSE);
- } catch (Exception e) {
- Log.e(TAG, "Error unducking player " + piid, e);
- } finally {
- playersToRemove.add(piid);
- }
- }
- } else {
- // this piid was in the list of ducked players, but wasn't found, discard it
- Log.v(TAG, "Error unducking player " + piid + ", player not found");
- playersToRemove.add(piid);
- }
- }
- for (int piid : playersToRemove) {
- mDuckedPlayers.remove(new Integer(piid));
- }
- mDuckedUids.remove(new Integer(winner.getClientUid()));
+ mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
}
}
@@ -541,4 +483,118 @@
mDispatcherCb.asBinder().unlinkToDeath(this, 0);
}
}
+
+ //=================================================================
+ // Class to handle ducking related operations for a given UID
+ private static final class DuckingManager {
+ private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
+
+ void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
+ if (!mDuckers.containsKey(uid)) {
+ mDuckers.put(uid, new DuckedApp(uid));
+ }
+ final DuckedApp da = mDuckers.get(uid);
+ for (AudioPlaybackConfiguration apc : apcsToDuck) {
+ da.addDuck(apc);
+ }
+ }
+
+ void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+ final DuckedApp da = mDuckers.remove(uid);
+ if (da == null) {
+ return;
+ }
+ da.removeUnduckAll(players);
+ }
+
+ // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+ void checkDuck(@NonNull AudioPlaybackConfiguration apc) {
+ final DuckedApp da = mDuckers.get(apc.getClientUid());
+ if (da == null) {
+ return;
+ }
+ // FIXME here the player needs to be put in a state that is the same as if it
+ // had been ducked as it starts. At the moment, this works already for linked
+ // players, as is the case in gapless playback.
+ da.addDuck(apc);
+ }
+
+ void dump(PrintWriter pw) {
+ for (DuckedApp da : mDuckers.values()) {
+ da.dump(pw);
+ }
+ }
+
+ void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ final DuckedApp da = mDuckers.get(apc.getClientUid());
+ if (da == null) {
+ return;
+ }
+ da.removeReleased(apc);
+ }
+
+ private static final class DuckedApp {
+ private final int mUid;
+ private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
+
+ DuckedApp(int uid) {
+ mUid = uid;
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("\t uid:" + mUid + " piids:");
+ for (int piid : mDuckedPlayers) {
+ pw.print(" " + piid);
+ }
+ pw.println("");
+ }
+
+ // pre-conditions:
+ // * apc != null
+ // * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+ void addDuck(@NonNull AudioPlaybackConfiguration apc) {
+ final int piid = new Integer(apc.getPlayerInterfaceId());
+ if (mDuckedPlayers.contains(piid)) {
+ if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
+ return;
+ }
+ try {
+ Log.v(TAG, "ducking player " + apc.getPlayerInterfaceId());
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_VSHAPE,
+ PLAY_CREATE_IF_NEEDED);
+ mDuckedPlayers.add(piid);
+ } catch (Exception e) {
+ Log.e(TAG, "Error ducking player " + piid, e);
+ }
+ }
+
+ void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
+ for (int piid : mDuckedPlayers) {
+ final AudioPlaybackConfiguration apc = players.get(piid);
+ if (apc != null) {
+ try {
+ Log.v(TAG, "unducking player " + piid);
+ apc.getPlayerProxy().applyVolumeShaper(
+ DUCK_ID,
+ VolumeShaper.Operation.REVERSE);
+ } catch (Exception e) {
+ Log.e(TAG, "Error unducking player " + piid, e);
+ }
+ } else {
+ // this piid was in the list of ducked players, but wasn't found
+ if (DEBUG) {
+ Log.v(TAG, "Error unducking player " + piid + ", player not found for"
+ + " uid " + mUid);
+ }
+ }
+ }
+ mDuckedPlayers.clear();
+ }
+
+ void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
index 6610634..88a6bdd 100644
--- a/services/core/java/com/android/server/fingerprint/RemovalClient.java
+++ b/services/core/java/com/android/server/fingerprint/RemovalClient.java
@@ -91,7 +91,7 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Removed:", e);
}
- return fingerId == 0;
+ return remaining == 0;
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index f92bf3d..4981d5c 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -66,7 +66,11 @@
@Override
public void applyChangesLocked(NotificationRecord record) {
- record.setRecentlyIntrusive(false);
+ // there will be another reconsideration in the message queue HANG_TIME_MS
+ // from each time this record alerts, which can finally clear this flag.
+ if ((System.currentTimeMillis() - record.getLastIntrusive()) >= HANG_TIME_MS) {
+ record.setRecentlyIntrusive(false);
+ }
}
};
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48d11c3..1d843c1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -884,6 +884,8 @@
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mZenModeHelper.onUserRemoved(user);
+ mRankingHelper.onUserRemoved(user);
+ savePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mConditionProviders.onUserUnlocked(user);
@@ -3199,10 +3201,8 @@
+ ", incomingUserId=" + incomingUserId
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
- // STOPSHIP TODO: should throw instead of logging or toasting.
- // throw new IllegalArgumentException(noChannelStr);
Log.e(TAG, noChannelStr);
- doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" +
+ doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
"Failed to post notification on channel \"" + channelId + "\"\n" +
"See log for more details");
return;
@@ -3238,8 +3238,10 @@
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
- private void doDebugOnlyToast(CharSequence toastText) {
- if (Build.IS_DEBUGGABLE) {
+ private void doChannelWarningToast(CharSequence toastText) {
+ final boolean warningEnabled = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 0) != 0;
+ if (warningEnabled || Build.IS_DEBUGGABLE) {
try {
Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG);
toast.show();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 90257da..f019a5c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -85,6 +85,7 @@
// to communicate with the ranking module.
private float mContactAffinity;
private boolean mRecentlyIntrusive;
+ private long mLastIntrusive;
// is this notification currently being intercepted by Zen Mode?
private boolean mIntercept;
@@ -515,12 +516,19 @@
public void setRecentlyIntrusive(boolean recentlyIntrusive) {
mRecentlyIntrusive = recentlyIntrusive;
+ if (recentlyIntrusive) {
+ mLastIntrusive = System.currentTimeMillis();
+ }
}
public boolean isRecentlyIntrusive() {
return mRecentlyIntrusive;
}
+ public long getLastIntrusive() {
+ return mLastIntrusive;
+ }
+
public void setPackagePriority(int packagePriority) {
mPackagePriority = packagePriority;
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3481556..1e741de 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -1040,6 +1040,18 @@
return packageChannels;
}
+ public void onUserRemoved(int userId) {
+ synchronized (mRecords) {
+ int N = mRecords.size();
+ for (int i = N - 1; i >= 0 ; i--) {
+ Record record = mRecords.valueAt(i);
+ if (UserHandle.getUserId(record.uid) == userId) {
+ mRecords.removeAt(i);
+ }
+ }
+ }
+ }
+
public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
int[] uidList) {
if (pkgList == null || pkgList.length == 0) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index f94a56f..59bce8f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -634,9 +634,13 @@
@Override
protected int adjustDexoptNeeded(int dexoptNeeded) {
- // Ensure compilation, no matter the current state.
- // TODO: The return value is wrong when patchoat is needed.
- return DexFile.DEX2OAT_FROM_SCRATCH;
+ if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+ // Ensure compilation by pretending a compiler filter change on the
+ // apk/odex location (the reason for the '-'. A positive value means
+ // the 'oat' location).
+ return -DexFile.DEX2OAT_FOR_FILTER;
+ }
+ return dexoptNeeded;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cfcd0a5..121ae07 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18514,11 +18514,6 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- Log.i(TAG, "Package doesn't exist in set block uninstall " + packageName);
- return false;
- }
// Cannot block uninstall of static shared libs as they are
// considered a part of the using app (emulating static linking).
// Also static libs are installed always on internal storage.
@@ -18528,12 +18523,7 @@
+ " providing static shared library: " + pkg.staticSharedLibName);
return false;
}
- if (!ps.getInstalled(userId)) {
- // Can't block uninstall for an app that is not installed or enabled.
- Log.i(TAG, "Package not installed in set block uninstall " + packageName);
- return false;
- }
- ps.setBlockUninstall(blockUninstall, userId);
+ mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
mSettings.writePackageRestrictionsLPr(userId);
}
return true;
@@ -18542,12 +18532,7 @@
@Override
public boolean getBlockUninstallForUser(String packageName, int userId) {
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- Log.i(TAG, "Package doesn't exist in get block uninstall " + packageName);
- return false;
- }
- return ps.getBlockUninstall(userId);
+ return mSettings.getBlockUninstallLPr(userId, packageName);
}
}
@@ -18754,7 +18739,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- false /*blockUninstall*/,
ps.readUserState(nextUserId).domainVerificationStatus,
0, PackageManager.INSTALL_REASON_UNKNOWN);
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index dfed72f..14f65eb 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -402,14 +402,6 @@
modifyUserState(userId).suspended = suspended;
}
- boolean getBlockUninstall(int userId) {
- return readUserState(userId).blockUninstall;
- }
-
- void setBlockUninstall(boolean blockUninstall, int userId) {
- modifyUserState(userId).blockUninstall = blockUninstall;
- }
-
boolean getInstantApp(int userId) {
return readUserState(userId).instantApp;
}
@@ -421,8 +413,8 @@
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
- ArraySet<String> disabledComponents, boolean blockUninstall,
- int domainVerifState, int linkGeneration, int installReason) {
+ ArraySet<String> disabledComponents, int domainVerifState,
+ int linkGeneration, int installReason) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -434,7 +426,6 @@
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
- state.blockUninstall = blockUninstall;
state.domainVerificationStatus = domainVerifState;
state.appLinkGeneration = linkGeneration;
state.installReason = installReason;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index cea031e..44bcff2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -186,6 +186,8 @@
private static final String TAG_PERMISSIONS = "perms";
private static final String TAG_CHILD_PACKAGE = "child-package";
private static final String TAG_USES_STATIC_LIB = "uses-static-lib";
+ private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages";
+ private static final String TAG_BLOCK_UNINSTALL = "block-uninstall";
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
@@ -215,6 +217,8 @@
// New name for the above attribute.
private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_SUSPENDED = "suspended";
+ // Legacy, uninstall blocks are stored separately.
+ @Deprecated
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_ENABLED_CALLER = "enabledCaller";
@@ -271,6 +275,9 @@
private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
new ArrayMap<String, PackageSetting>();
+ /** List of packages that are blocked for uninstall for specific users */
+ private final SparseArray<ArraySet<String>> mBlockUninstallPackages = new SparseArray<>();
+
// Set of restored intent-filter verification states
private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
new ArrayMap<String, IntentFilterVerificationInfo>();
@@ -756,7 +763,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- false /*blockUninstall*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
0, PackageManager.INSTALL_REASON_UNKNOWN);
}
@@ -1614,6 +1620,34 @@
}
}
+ void readBlockUninstallPackagesLPw(XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ ArraySet<String> packages = new ArraySet<>();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_BLOCK_UNINSTALL)) {
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ packages.add(packageName);
+ } else {
+ String msg = "Unknown element under " + TAG_BLOCK_UNINSTALL_PACKAGES + ": " +
+ parser.getName();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ if (packages.isEmpty()) {
+ mBlockUninstallPackages.remove(userId);
+ } else {
+ mBlockUninstallPackages.put(userId, packages);
+ }
+ }
+
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1662,7 +1696,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- false /*blockUninstall*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
0, PackageManager.INSTALL_REASON_UNKNOWN);
}
@@ -1768,9 +1801,12 @@
}
}
+ if (blockUninstall) {
+ setBlockUninstallLPw(userId, name, true);
+ }
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, instantApp, enabledCaller, enabledComponents,
- disabledComponents, blockUninstall, verifState, linkGeneration,
+ disabledComponents, verifState, linkGeneration,
installReason);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -1780,6 +1816,8 @@
readCrossProfileIntentFiltersLPw(parser, userId);
} else if (tagName.equals(TAG_DEFAULT_APPS)) {
readDefaultAppsLPw(parser, userId);
+ } else if (tagName.equals(TAG_BLOCK_UNINSTALL_PACKAGES)) {
+ readBlockUninstallPackagesLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1806,6 +1844,30 @@
}
}
+ void setBlockUninstallLPw(int userId, String packageName, boolean blockUninstall) {
+ ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+ if (blockUninstall) {
+ if (packages == null) {
+ packages = new ArraySet<String>();
+ mBlockUninstallPackages.put(userId, packages);
+ }
+ packages.add(packageName);
+ } else if (packages != null) {
+ packages.remove(packageName);
+ if (packages.isEmpty()) {
+ mBlockUninstallPackages.remove(userId);
+ }
+ }
+ }
+
+ boolean getBlockUninstallLPr(int userId, String packageName) {
+ ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+ if (packages == null) {
+ return false;
+ }
+ return packages.contains(packageName);
+ }
+
private ArraySet<String> readComponentsLPr(XmlPullParser parser)
throws IOException, XmlPullParserException {
ArraySet<String> components = null;
@@ -1976,6 +2038,20 @@
serializer.endTag(null, TAG_DEFAULT_APPS);
}
+ void writeBlockUninstallPackagesLPr(XmlSerializer serializer, int userId)
+ throws IOException {
+ ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+ if (packages != null) {
+ serializer.startTag(null, TAG_BLOCK_UNINSTALL_PACKAGES);
+ for (int i = 0; i < packages.size(); i++) {
+ serializer.startTag(null, TAG_BLOCK_UNINSTALL);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packages.valueAt(i));
+ serializer.endTag(null, TAG_BLOCK_UNINSTALL);
+ }
+ serializer.endTag(null, TAG_BLOCK_UNINSTALL_PACKAGES);
+ }
+ }
+
void writePackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -2038,9 +2114,6 @@
if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true");
}
- if (ustate.blockUninstall) {
- serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
- }
if (ustate.instantApp) {
serializer.attribute(null, ATTR_INSTANT_APP, "true");
}
@@ -2091,6 +2164,7 @@
writePersistentPreferredActivitiesLPr(serializer, userId);
writeCrossProfileIntentFiltersLPr(serializer, userId);
writeDefaultAppsLPr(serializer, userId);
+ writeBlockUninstallPackagesLPr(serializer, userId);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f4f7e24..8e06a51 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -325,8 +325,6 @@
agentInfo.label = resolveInfo.loadLabel(pm);
agentInfo.icon = resolveInfo.loadIcon(pm);
agentInfo.settings = getSettingsAttrs(pm, resolveInfo);
- agentInfo.agent = new TrustAgentWrapper(mContext, this,
- new Intent().setComponent(name), userInfo.getUserHandle());
} else {
int index = mActiveAgents.indexOf(agentInfo);
agentInfo = mActiveAgents.valueAt(index);
@@ -363,6 +361,11 @@
}
}
+ if (agentInfo.agent == null) {
+ agentInfo.agent = new TrustAgentWrapper(mContext, this,
+ new Intent().setComponent(name), userInfo.getUserHandle());
+ }
+
if (!mActiveAgents.contains(agentInfo)) {
mActiveAgents.add(agentInfo);
} else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index be242b6..b27d6c4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2891,21 +2891,6 @@
final Rect frame = new Rect();
final Rect stackBounds = new Rect();
- boolean includeImeInScreenshot;
- synchronized(mService.mWindowMap) {
- final AppWindowToken imeTargetAppToken = mService.mInputMethodTarget != null
- ? mService.mInputMethodTarget.mAppToken : null;
- // We only include the Ime in the screenshot if the app we are screenshoting is the IME
- // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
- // mode because the frame of the IME might not overlap with that of the app.
- // E.g. IME target app at the top in split-screen mode and the IME at the bottom
- // overlapping with the bottom app.
- includeImeInScreenshot = imeTargetAppToken != null
- && imeTargetAppToken.appToken != null
- && imeTargetAppToken.appToken.asBinder() == appToken
- && !mService.mInputMethodTarget.isInMultiWindowMode();
- }
-
final int aboveAppLayer = (mService.mPolicy.getWindowLayerFromTypeLw(TYPE_APPLICATION) + 1)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
final MutableBoolean mutableIncludeFullDisplay = new MutableBoolean(includeFullDisplay);
@@ -2923,9 +2908,7 @@
return false;
}
if (w.mIsImWindow) {
- if (!includeImeInScreenshot) {
- return false;
- }
+ return false;
} else if (w.mIsWallpaper) {
// If this is the wallpaper layer and we're only looking for the wallpaper layer
// then the target window state is this one.
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index e2428b9..8ab6d08 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -843,6 +843,36 @@
}
@Test
+ public void testOnUserRemoved() throws Exception {
+ int[] user0Uids = {98, 235, 16, 3782};
+ int[] user1Uids = new int[user0Uids.length];
+ for (int i = 0; i < user0Uids.length; i++) {
+ user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
+
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+
+ // create records with the default channel for all user 0 and user 1 uids
+ mHelper.getImportance(PKG, user0Uids[i]);
+ mHelper.getImportance(PKG, user1Uids[i]);
+ }
+
+ mHelper.onUserRemoved(1);
+
+ // user 0 records remain
+ for (int i = 0; i < user0Uids.length; i++) {
+ assertEquals(1,
+ mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
+ }
+ // user 1 records are gone
+ for (int i = 0; i < user1Uids.length; i++) {
+ assertEquals(0,
+ mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
+ }
+ }
+
+ @Test
public void testOnPackageChanged_packageRemoval() throws Exception {
// Deleted
NotificationChannel channel1 =
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 325d99a..5e4ba7be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -31,13 +31,10 @@
import static org.junit.Assert.fail;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.UserInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.support.test.InstrumentationRegistry;
@@ -49,26 +46,17 @@
import android.util.LongSparseArray;
import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastPrintWriter;
import com.android.server.LocalServices;
-import org.hamcrest.core.IsNot;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.PrintStream;
import java.security.PublicKey;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -547,7 +535,6 @@
private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) {
- assertThat(userState.blockUninstall, is(false));
assertThat(userState.enabled, is(0));
assertThat(userState.hidden, is(false));
assertThat(userState.installed, is(installed));
@@ -783,31 +770,14 @@
@Before
public void createUserManagerServiceRef() throws ReflectiveOperationException {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- Constructor<UserManagerService> umsc;
- try {
- // unregister the user manager from the local service
- Method removeServiceForTest = LocalServices.class.getDeclaredMethod(
- "removeServiceForTest", Class.class);
- removeServiceForTest.invoke(null, UserManagerInternal.class);
-
- // now create a new user manager [which registers again with the local service]
- umsc = UserManagerService.class.getDeclaredConstructor(
- Context.class,
- PackageManagerService.class,
- Object.class,
- File.class);
- umsc.setAccessible(true);
- UserManagerService ums = umsc.newInstance(InstrumentationRegistry.getContext(),
- null /*PackageManagerService*/, new Object() /*packagesLock*/,
- new File(InstrumentationRegistry.getContext().getFilesDir(), "user"));
- } catch (SecurityException
- | ReflectiveOperationException
- | IllegalArgumentException e) {
- fail("Could not create user manager service; " + e);
- }
+ InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
+ try {
+ // unregister the user manager from the local service
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ new UserManagerService(InstrumentationRegistry.getContext());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Could not create user manager service; " + e);
}
});
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index bf05f21..50be8db 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -49,10 +49,6 @@
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserState();
- oldUserState.blockUninstall = true;
- assertThat(testUserState.equals(oldUserState), is(false));
-
- oldUserState = new PackageUserState();
oldUserState.ceDataInode = 4000L;
assertThat(testUserState.equals(oldUserState), is(false));
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
index 38ea6e6f..a75cd86 100644
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java
@@ -120,7 +120,7 @@
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
incomingCallIntent, listener);
}
@@ -129,7 +129,7 @@
@Override
public void endSession(int sessionId) throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
}
}
@@ -138,7 +138,7 @@
public boolean isConnected(int callServiceType, int callType)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
callServiceType, callType);
}
@@ -147,7 +147,7 @@
@Override
public boolean isOpened() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
}
}
@@ -156,7 +156,7 @@
public void addRegistrationListener(IImsRegistrationListener listener)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
listener);
}
@@ -166,7 +166,7 @@
public void removeRegistrationListener(IImsRegistrationListener listener)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
listener);
}
@@ -176,7 +176,7 @@
public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
sessionId, callServiceType, callType);
}
@@ -186,7 +186,7 @@
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
sessionId, profile, listener);
}
@@ -196,7 +196,7 @@
public IImsCallSession getPendingCallSession(int sessionId, String callId)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
sessionId, callId);
}
@@ -205,7 +205,7 @@
@Override
public IImsUt getUtInterface() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
}
}
@@ -213,7 +213,7 @@
@Override
public IImsConfig getConfigInterface() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
}
}
@@ -221,7 +221,7 @@
@Override
public void turnOnIms() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
}
}
@@ -229,7 +229,7 @@
@Override
public void turnOffIms() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
}
}
@@ -237,7 +237,7 @@
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
}
}
@@ -246,7 +246,7 @@
public void setUiTTYMode(int uiTtyMode, Message onComplete)
throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
onComplete);
}
@@ -255,7 +255,7 @@
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
synchronized (mLock) {
- checkBinderConnection();
+ checkServiceIsReady();
return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
mSupportedFeature);
}
@@ -264,7 +264,8 @@
@Override
public int getFeatureStatus() {
synchronized (mLock) {
- if (mFeatureStatusCached != null) {
+ if (isBinderAlive() && mFeatureStatusCached != null) {
+ Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
return mFeatureStatusCached;
}
}
@@ -277,6 +278,7 @@
// Cache only non-null value for feature status.
mFeatureStatusCached = status;
}
+ Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
return status;
}
@@ -301,10 +303,28 @@
mStatusCallback = c;
}
+ /**
+ * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
+ * method returns false, it doesn't mean that the Binder connection is not available (use
+ * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
+ * at this time.
+ *
+ * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
+ * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
+ */
+ public boolean isBinderReady() {
+ return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
+ }
+
@Override
public boolean isBinderAlive() {
- return mIsAvailable && getFeatureStatus() == ImsFeature.STATE_READY && mBinder != null &&
- mBinder.isBinderAlive();
+ return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
+ }
+
+ protected void checkServiceIsReady() throws RemoteException {
+ if (!isBinderReady()) {
+ throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
+ }
}
private IImsServiceController getServiceInterface(IBinder b) {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 988dd58..395f1cc 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -150,7 +150,7 @@
private void notifyFeatureState(@ImsState int state) {
if (mStatusCallback != null) {
try {
- Log.i(LOG_TAG, "notifying ImsFeatureState");
+ Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
mStatusCallback.notifyImsFeatureStatus(state);
} catch (RemoteException e) {
mStatusCallback = null;