Merge "Remove LOCAL_SRC_FILES from RRO modules" into stage-aosp-master
am: 651b6ee8c5
Change-Id: I02487eee98057e0358c3136e27c2ac181b0264e7
diff --git a/api/test-current.txt b/api/test-current.txt
index d2dbacb..fa018a3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2313,6 +2313,7 @@
}
public static interface DeviceConfig.WindowManager {
+ field public static final String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "system_gestures_excluded_by_pre_q_sticky_immersive";
field public static final String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 60f1424..2f0782b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2507,7 +2507,7 @@
}
/**
- * @return The duration of the operation in milliseconds.
+ * @return The duration of the operation in milliseconds. The duration is in wall time.
*/
public long getDuration() {
return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
@@ -2515,7 +2515,7 @@
/**
* Return the duration in milliseconds the app accessed this op while
- * in the foreground.
+ * in the foreground. The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -2534,7 +2534,7 @@
/**
* Return the duration in milliseconds the app accessed this op while
- * in the background.
+ * in the background. The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -2553,7 +2553,7 @@
/**
* Return the duration in milliseconds the app accessed this op for
- * a given range of UID states.
+ * a given range of UID states. The duration is in wall time.
*
* @param fromUidState The UID state for which to query. Could be one of
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
@@ -3968,6 +3968,7 @@
/**
* Gets the total duration the app op was accessed (performed) in the foreground.
+ * The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -3986,6 +3987,7 @@
/**
* Gets the total duration the app op was accessed (performed) in the background.
+ * The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -4004,7 +4006,7 @@
/**
* Gets the total duration the app op was accessed (performed) for a given
- * range of UID states.
+ * range of UID states. The duration is in wall time.
*
* @param fromUidState The UID state from which to query. Could be one of
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 2d18838..bb775fc 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -17,6 +17,7 @@
package android.app.servertransaction;
import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import android.annotation.UnsupportedAppUsage;
import android.app.ClientTransactionHandler;
@@ -38,10 +39,11 @@
@UnsupportedAppUsage
private List<ReferrerIntent> mIntents;
+ private boolean mResume;
@Override
public int getPostExecutionState() {
- return ON_RESUME;
+ return mResume ? ON_RESUME : UNDEFINED;
}
@Override
@@ -58,12 +60,13 @@
private NewIntentItem() {}
/** Obtain an instance initialized with provided params. */
- public static NewIntentItem obtain(List<ReferrerIntent> intents) {
+ public static NewIntentItem obtain(List<ReferrerIntent> intents, boolean resume) {
NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class);
if (instance == null) {
instance = new NewIntentItem();
}
instance.mIntents = intents;
+ instance.mResume = resume;
return instance;
}
@@ -71,6 +74,7 @@
@Override
public void recycle() {
mIntents = null;
+ mResume = false;
ObjectPool.recycle(this);
}
@@ -80,11 +84,13 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mResume);
dest.writeTypedList(mIntents, flags);
}
/** Read from Parcel. */
private NewIntentItem(Parcel in) {
+ mResume = in.readBoolean();
mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
}
@@ -108,18 +114,19 @@
return false;
}
final NewIntentItem other = (NewIntentItem) o;
- return Objects.equals(mIntents, other.mIntents);
+ return mResume == other.mResume && Objects.equals(mIntents, other.mIntents);
}
@Override
public int hashCode() {
int result = 17;
+ result = 31 * result + (mResume ? 1 : 0);
result = 31 * result + mIntents.hashCode();
return result;
}
@Override
public String toString() {
- return "NewIntentItem{intents=" + mIntents + "}";
+ return "NewIntentItem{intents=" + mIntents + ",resume=" + mResume + "}";
}
}
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b0b1874..23fbefb 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -17,7 +17,6 @@
package android.content.pm;
import android.Manifest;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -177,8 +176,7 @@
mContext.registerReceiver(mUserRemovedReceiver, userFilter);
}
- @VisibleForTesting
- protected void handlePackageEvent(Intent intent, int userId) {
+ private void handlePackageEvent(Intent intent, int userId) {
// Don't regenerate the services map when the package is removed or its
// ASEC container unmounted as a step in replacement. The subsequent
// _ADDED / _AVAILABLE call will regenerate the map in the final state.
@@ -240,9 +238,6 @@
public void invalidateCache(int userId) {
synchronized (mServicesLock) {
- if (DEBUG) {
- Slog.d(TAG, "invalidating cache for " + userId + " " + mInterfaceName);
- }
final UserServices<V> user = findOrCreateUserLocked(userId);
user.services = null;
onServicesChangedLocked(userId);
@@ -472,48 +467,34 @@
* or null to assume that everything is affected.
* @param userId the user for whom to update the services map.
*/
- private void generateServicesMap(@Nullable int[] changedUids, int userId) {
+ private void generateServicesMap(int[] changedUids, int userId) {
if (DEBUG) {
Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
+ Arrays.toString(changedUids));
}
+ final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
+ final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ try {
+ ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+ if (info == null) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
+ continue;
+ }
+ serviceInfos.add(info);
+ } catch (XmlPullParserException | IOException e) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
+ }
+ }
+
synchronized (mServicesLock) {
final UserServices<V> user = findOrCreateUserLocked(userId);
- final boolean cacheInvalid = user.services == null;
- if (cacheInvalid) {
+ final boolean firstScan = user.services == null;
+ if (firstScan) {
user.services = Maps.newHashMap();
}
- final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
- final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
-
- for (ResolveInfo resolveInfo : resolveInfos) {
- try {
- // when changedUids == null, we want to do a rescan of everything, this means
- // it's the initial scan, and containsUid will trivially return true
- // when changedUids != null, we got here because a package changed, but
- // invalidateCache could have been called (thus user.services == null), and we
- // should query from PackageManager again
- if (!cacheInvalid
- && !containsUid(
- changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) {
- if (DEBUG) {
- Slog.d(TAG, "Skipping parseServiceInfo for " + resolveInfo);
- }
- continue;
- }
- ServiceInfo<V> info = parseServiceInfo(resolveInfo);
- if (info == null) {
- Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
- continue;
- }
- serviceInfos.add(info);
- } catch (XmlPullParserException | IOException e) {
- Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
- }
- }
-
StringBuilder changes = new StringBuilder();
boolean changed = false;
for (ServiceInfo<V> info : serviceInfos) {
@@ -534,7 +515,7 @@
changed = true;
user.services.put(info.type, info);
user.persistentServices.put(info.type, info.uid);
- if (!(user.mPersistentServicesFileDidNotExist && cacheInvalid)) {
+ if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
notifyListener(info.type, userId, false /* removed */);
}
} else if (previousUid == info.uid) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 920eb4b..e30ba38 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -325,6 +325,17 @@
*/
@TestApi
String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
+
+ /**
+ * Key for controlling whether system gestures are implicitly excluded by windows requesting
+ * sticky immersive mode from apps that are targeting an SDK prior to Q.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
+ * @hide
+ */
+ @TestApi
+ String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE =
+ "system_gestures_excluded_by_pre_q_sticky_immersive";
}
private static final Object sLock = new Object();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f9d27bb..00206fc 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1534,7 +1534,11 @@
if (driList.get(i).getResolvedComponentName().equals(
resultList.get(j).getTargetComponent())) {
ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j);
- ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo);
+ // Incoming results are ordered but without a score. Create a score
+ // based on the index in order to be sorted appropriately when joined
+ // with legacy direct share api results.
+ float score = Math.max(1.0f - (0.05f * j), 0.0f);
+ ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score);
chooserTargets.add(chooserTarget);
if (mDirectShareAppTargetCache != null && appTargets != null) {
mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
@@ -1580,7 +1584,8 @@
return false;
}
- private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) {
+ private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut,
+ float score) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
Bundle extras = new Bundle();
extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
@@ -1591,7 +1596,7 @@
null,
// The ranking score for this target (0.0-1.0); the system will omit items with low
// scores when there are too many Direct Share items.
- 1.0f,
+ score,
// The name of the component to be launched if this target is chosen.
shareShortcut.getTargetComponent().clone(),
// The extra values here will be merged into the Intent when this target is chosen.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f905ea2..58ce03b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -889,7 +889,8 @@
: mAdapterView.getCheckedItemPosition();
boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem();
ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
- if (!ri.handleAllWebDataURI && id == R.id.button_always) {
+ if (mUseLayoutForBrowsables
+ && !ri.handleAllWebDataURI && id == R.id.button_always) {
showSettingsForSelected(ri);
} else {
startSelected(which, id == R.id.button_always, hasIndexBeenFiltered);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6f4f337..fe66cf9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1311,7 +1311,7 @@
return semiTransparentBarColor;
} else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
return Color.BLACK;
- } else if (scrimTransparent && barColor == Color.TRANSPARENT) {
+ } else if (scrimTransparent && Color.alpha(barColor) == 0) {
boolean light = (sysuiVis & lightSysuiFlag) != 0;
return light ? SCRIM_LIGHT : semiTransparentBarColor;
} else {
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 711eaa7..c50cbe3 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -19,6 +19,8 @@
import static android.content.Intent.ACTION_EDIT;
import static android.content.Intent.ACTION_VIEW;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
@@ -31,6 +33,7 @@
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.content.Intent;
@@ -45,9 +48,13 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.content.ReferrerIntent;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -307,6 +314,24 @@
assertEquals(400, activity.mConfig.smallestScreenWidthDp);
}
+ @Test
+ public void testResumeAfterNewIntent() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final ArrayList<ReferrerIntent> rIntents = new ArrayList<>();
+ rIntents.add(new ReferrerIntent(new Intent(), "android.app.activity"));
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false));
+ });
+ assertThat(activity.isResumed()).isFalse();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true));
+ });
+ assertThat(activity.isResumed()).isTrue();
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)}
* to try to push activity configuration to the activity for the given sequence number.
@@ -386,6 +411,16 @@
return transaction;
}
+ private static ClientTransaction newNewIntentTransaction(Activity activity,
+ List<ReferrerIntent> intents, boolean resume) {
+ final NewIntentItem item = NewIntentItem.obtain(intents, resume);
+
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addCallback(item);
+
+ return transaction;
+ }
+
private static ClientTransaction newTransaction(Activity activity) {
final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
return ClientTransaction.obtain(appThread, activity.getActivityToken());
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 1e49c0a..37d21f0 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -214,15 +214,15 @@
@Test
public void testRecycleNewIntentItem() {
- NewIntentItem emptyItem = NewIntentItem.obtain(null);
- NewIntentItem item = NewIntentItem.obtain(referrerIntentList());
+ NewIntentItem emptyItem = NewIntentItem.obtain(null, false);
+ NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), false);
assertNotSame(item, emptyItem);
assertFalse(item.equals(emptyItem));
item.recycle();
assertEquals(item, emptyItem);
- NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList());
+ NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList(), false);
assertSame(item, item2);
assertFalse(item2.equals(emptyItem));
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 36ed88f..d2b18cb 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -128,7 +128,7 @@
@Test
public void testNewIntent() {
// Write to parcel
- NewIntentItem item = NewIntentItem.obtain(referrerIntentList());
+ NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), false);
writeAndPrepareForReading(item);
// Read from parcel and assert
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index c8150b1..365e97d 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.content.Intent;
import android.content.res.Resources;
import android.os.FileUtils;
import android.os.Parcel;
@@ -190,36 +189,6 @@
assertEquals(0, cache.getPersistentServicesSize(u1));
}
- /**
- * Check that an optimization to skip a call to PackageManager handles an invalidated cache.
- *
- * We added an optimization in generateServicesMap to only query PackageManager for packages
- * that have been changed, because if a package is unchanged, we have already cached the
- * services info for it, so we can save a query to PackageManager (and save some memory).
- * However, if invalidateCache was called, we cannot optimize, and must do a full query.
- * The initial optimization was buggy because it failed to check for an invalidated cache, and
- * only scanned the changed packages, given in the ACTION_PACKAGE_CHANGED intent (b/122912184).
- */
- public void testParseServiceInfoOptimizationHandlesInvalidatedCache() {
- TestServicesCache cache = new TestServicesCache();
- cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
- cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
- assertEquals(2, cache.getAllServicesSize(U0));
-
- // simulate the client of the cache invalidating it
- cache.invalidateCache(U0);
-
- // there should be 0 services (userServices.services == null ) at this point, but we don't
- // call getAllServicesSize since that would force a full scan of packages,
- // instead we trigger a package change in a package that is in the list of services
- Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
- intent.putExtra(Intent.EXTRA_UID, UID1);
- cache.handlePackageEvent(intent, U0);
-
- // check that the optimization does a full query and caches both services
- assertEquals(2, cache.getAllServicesSize(U0));
- }
-
private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
TestServiceType type, int uid) {
final ComponentInfo info = new ComponentInfo();
@@ -297,11 +266,6 @@
map = new HashMap<>();
mServices.put(userId, map);
}
- // in actual cases, resolveInfo should always have a serviceInfo, since we specifically
- // query for intent services
- resolveInfo.serviceInfo = new android.content.pm.ServiceInfo();
- resolveInfo.serviceInfo.applicationInfo =
- new ApplicationInfo(serviceInfo.componentInfo.applicationInfo);
map.put(resolveInfo, serviceInfo);
}
@@ -340,11 +304,6 @@
public void onUserRemoved(int userId) {
super.onUserRemoved(userId);
}
-
- @Override
- public void handlePackageEvent(Intent intent, int userId) {
- super.handlePackageEvent(intent, userId);
- }
}
static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp
index df0dd7c..f1ab149 100644
--- a/libs/androidfw/PosixUtils.cpp
+++ b/libs/androidfw/PosixUtils.cpp
@@ -64,6 +64,9 @@
return nullptr;
}
+ auto gid = getgid();
+ auto uid = getuid();
+
char const** argv0 = (char const**)malloc(sizeof(char*) * (argv.size() + 1));
for (size_t i = 0; i < argv.size(); i++) {
argv0[i] = argv[i].c_str();
@@ -75,6 +78,16 @@
PLOG(ERROR) << "fork";
return nullptr;
case 0: // child
+ if (setgid(gid) != 0) {
+ PLOG(ERROR) << "setgid";
+ exit(1);
+ }
+
+ if (setuid(uid) != 0) {
+ PLOG(ERROR) << "setuid";
+ exit(1);
+ }
+
close(stdout[0]);
if (dup2(stdout[1], STDOUT_FILENO) == -1) {
abort();
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 63a4510..c3dd3fe 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -46,14 +46,14 @@
import java.util.concurrent.Executor;
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ *
* Allows an app to interact with an active {@link MediaSession2} or a
* {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
* commands can be sent to the session.
- * <p>
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
*/
public class MediaController2 implements AutoCloseable {
static final String TAG = "MediaController2";
@@ -405,6 +405,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Builder for {@link MediaController2}.
* <p>
* Any incoming event from the {@link MediaSession2} will be handled on the callback
@@ -502,9 +507,12 @@
}
/**
- * Interface for listening to change in activeness of the {@link MediaSession2}.
- * <p>
* This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Interface for listening to change in activeness of the {@link MediaSession2}.
*/
public abstract static class ControllerCallback {
/**
diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java
index b3edf3f..081e76a 100644
--- a/media/apex/java/android/media/MediaSession2.java
+++ b/media/apex/java/android/media/MediaSession2.java
@@ -52,13 +52,13 @@
import java.util.concurrent.Executor;
/**
- * Allows a media app to expose its transport controls and playback information in a process to
- * other processes including the Android framework and other apps.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Allows a media app to expose its transport controls and playback information in a process to
+ * other processes including the Android framework and other apps.
*/
public class MediaSession2 implements AutoCloseable {
static final String TAG = "MediaSession2";
@@ -481,6 +481,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Builder for {@link MediaSession2}.
* <p>
* Any incoming event from the {@link MediaController2} will be handled on the callback
@@ -616,9 +621,12 @@
}
/**
- * Information of a controller.
- * <p>
* This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Information of a controller.
*/
public static final class ControllerInfo {
private final RemoteUserInfo mRemoteUserInfo;
@@ -807,9 +815,12 @@
}
/**
- * Callback to be called for all incoming commands from {@link MediaController2}s.
- * <p>
* This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Callback to be called for all incoming commands from {@link MediaController2}s.
*/
public abstract static class SessionCallback {
/**
diff --git a/media/apex/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java
index ee584e5..f6fd509 100644
--- a/media/apex/java/android/media/MediaSession2Service.java
+++ b/media/apex/java/android/media/MediaSession2Service.java
@@ -44,12 +44,12 @@
import java.util.Map;
/**
- * Service containing {@link MediaSession2}.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Service containing {@link MediaSession2}.
*/
public abstract class MediaSession2Service extends Service {
/**
@@ -287,6 +287,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Returned by {@link #onUpdateNotification(MediaSession2)} for making session service
* foreground service to keep playback running in the background. It's highly recommended to
* show media style notification here.
diff --git a/media/apex/java/android/media/Session2Command.java b/media/apex/java/android/media/Session2Command.java
index 7c752e1..26f4568 100644
--- a/media/apex/java/android/media/Session2Command.java
+++ b/media/apex/java/android/media/Session2Command.java
@@ -26,6 +26,11 @@
import java.util.Objects;
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
* <p>
* If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
@@ -35,11 +40,6 @@
* Refer to the
* <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a>
* class for the list of valid commands.
- * <p>
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
*/
public final class Session2Command implements Parcelable {
/**
@@ -162,6 +162,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Contains the result of {@link Session2Command}.
*/
public static final class Result {
diff --git a/media/apex/java/android/media/Session2CommandGroup.java b/media/apex/java/android/media/Session2CommandGroup.java
index 06ae873..0ee5f62 100644
--- a/media/apex/java/android/media/Session2CommandGroup.java
+++ b/media/apex/java/android/media/Session2CommandGroup.java
@@ -28,13 +28,12 @@
import java.util.Set;
/**
- * A set of {@link Session2Command} which represents a command group.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
- * </p>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * A set of {@link Session2Command} which represents a command group.
*/
public final class Session2CommandGroup implements Parcelable {
private static final String TAG = "Session2CommandGroup";
@@ -131,6 +130,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Builds a {@link Session2CommandGroup} object.
*/
public static final class Builder {
diff --git a/media/apex/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java
index 6d499fa..6eb76b1 100644
--- a/media/apex/java/android/media/Session2Token.java
+++ b/media/apex/java/android/media/Session2Token.java
@@ -36,13 +36,13 @@
import java.util.Objects;
/**
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
- * If it's representing a session service, it may not be ongoing.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * If it's representing a session service, it may not be ongoing.
* <p>
* This may be passed to apps by the session owner to allow them to create a
* {@link MediaController2} to communicate with the session.
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 569d11e..dec0140 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -119,6 +119,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
* created.
* <p>
@@ -192,6 +197,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
* current user.
* <p>
@@ -335,12 +345,12 @@
}
/**
- * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
*
* @param listener The listener to add
*/
@@ -350,12 +360,12 @@
}
/**
- * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
*
* @param listener The listener to add
* @param handler The handler to call listener on.
@@ -366,12 +376,12 @@
}
/**
- * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
*
* @param userId The userId to listen for changes on
* @param listener The listener to add
@@ -402,6 +412,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
*
* @param listener The listener to remove.
@@ -765,13 +780,13 @@
}
/**
- * Listens for changes to the {@link #getSession2Tokens()}. This can be added
- * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Listens for changes to the {@link #getSession2Tokens()}. This can be added
+ * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
*/
public interface OnSession2TokensChangedListener {
/**
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 19e7b73..6e6c009 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -112,6 +112,9 @@
wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast
</string>
+ <!-- The minimum number of tiles to display in QuickSettings -->
+ <integer name="quick_settings_min_num_tiles">6</integer>
+
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8199ea3..591af82 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2009,7 +2009,7 @@
<string name="drag_to_remove_tiles">Drag here to remove</string>
<!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] -->
- <string name="drag_to_remove_disabled">You need at least 6 tiles</string>
+ <string name="drag_to_remove_disabled">You need at least <xliff:g id="min_num_tiles" example="6">%1$d</xliff:g> tiles</string>
<!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
<string name="qs_edit">Edit</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 8ed5424..2542abd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -54,7 +55,6 @@
import java.util.List;
public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
- private static final int MIN_NUM_TILES = 6;
private static final long DRAG_LENGTH = 100;
private static final float DRAG_SCALE = 1.2f;
public static final long MOVE_DURATION = 150;
@@ -79,6 +79,7 @@
private final ItemTouchHelper mItemTouchHelper;
private final ItemDecoration mDecoration;
private final AccessibilityManager mAccessibilityManager;
+ private final int mMinNumTiles;
private int mEditIndex;
private int mTileDividerIndex;
private boolean mNeedsFocus;
@@ -97,6 +98,7 @@
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
mDecoration = new TileItemDecoration(context);
+ mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
}
public void setHost(QSTileHost host) {
@@ -247,15 +249,17 @@
return;
}
if (holder.getItemViewType() == TYPE_EDIT) {
- final int titleResId;
+ final String titleText;
+ Resources res = mContext.getResources();
if (mCurrentDrag == null) {
- titleResId = R.string.drag_to_add_tiles;
+ titleText = res.getString(R.string.drag_to_add_tiles);
} else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) {
- titleResId = R.string.drag_to_remove_disabled;
+ titleText = res.getString(R.string.drag_to_remove_disabled, mMinNumTiles);
} else {
- titleResId = R.string.drag_to_remove_tiles;
+ titleText = res.getString(R.string.drag_to_remove_tiles);
}
- ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId);
+
+ ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleText);
return;
}
if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
@@ -337,7 +341,7 @@
}
private boolean canRemoveTiles() {
- return mCurrentSpecs.size() > MIN_NUM_TILES;
+ return mCurrentSpecs.size() > mMinNumTiles;
}
private void selectPosition(int position, View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index b70b45b..4049201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.content.res.Resources;
+import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
@@ -43,6 +46,7 @@
import java.util.Stack;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -58,6 +62,8 @@
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
private static final String TAG = "NotificationViewHierarchyManager";
+ private final Handler mHandler;
+
//TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -86,9 +92,13 @@
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
private boolean mPerformingUpdate;
+ // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
+ // the problem.
+ private boolean mIsHandleDynamicPrivacyChangeScheduled;
@Inject
public NotificationViewHierarchyManager(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
@@ -97,6 +107,7 @@
Lazy<ShadeController> shadeController,
BubbleData bubbleData,
DynamicPrivacyController privacyController) {
+ mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
mGroupManager = groupManager;
mVisualStabilityManager = visualStabilityManager;
@@ -435,19 +446,33 @@
@Override
public void onDynamicPrivacyChanged() {
+ if (mPerformingUpdate) {
+ Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
+ }
+ // This listener can be called from updateNotificationViews() via a convoluted listener
+ // chain, so we post here to prevent a re-entrant call. See b/136186188
+ // TODO: Refactor away the need for this
+ if (!mIsHandleDynamicPrivacyChangeScheduled) {
+ mIsHandleDynamicPrivacyChangeScheduled = true;
+ mHandler.post(this::onHandleDynamicPrivacyChanged);
+ }
+ }
+
+ private void onHandleDynamicPrivacyChanged() {
+ mIsHandleDynamicPrivacyChangeScheduled = false;
updateNotificationViews();
}
private void beginUpdate() {
if (mPerformingUpdate) {
- throw new IllegalStateException("Re-entrant code during update.");
+ Log.wtf(TAG, "Re-entrant code during update", new Exception());
}
mPerformingUpdate = true;
}
private void endUpdate() {
if (!mPerformingUpdate) {
- throw new IllegalStateException("Manager state has become desynced.");
+ Log.wtf(TAG, "Manager state has become desynced", new Exception());
}
mPerformingUpdate = false;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index c476d80..5103e8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -78,13 +82,19 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
private NotificationViewHierarchyManager mViewHierarchyManager;
private NotificationTestHelper mHelper;
+ private boolean mMadeReentrantCall = false;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ mTestableLooper = TestableLooper.get(this);
+ Assert.sMainLooper = mTestableLooper.getLooper();
+ mHandler = Handler.createAsync(mTestableLooper.getLooper());
+
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
@@ -97,7 +107,7 @@
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
- mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
() -> mShadeController, new BubbleData(mContext), mock(DynamicPrivacyController.class));
Dependency.get(InitController.class).executePostInitTasks();
@@ -212,9 +222,60 @@
verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
}
+ @Test
+ public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
+ // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews()
+ mViewHierarchyManager.updateNotificationViews();
+
+ // THEN onNotificationViewUpdateFinished() is only called once
+ verify(mListContainer).onNotificationViewUpdateFinished();
+
+ // WHEN we drain the looper
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called a second time (for the reentrant call)
+ verify(mListContainer, times(2)).onNotificationViewUpdateFinished();
+ }
+
+ @Test
+ public void testMultipleReentrantCallsToOnDynamicPrivacyChangedOnlyPostOnce() {
+ // GIVEN a ListContainer that will make many re-entrant calls to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews() and drain the looper
+ mViewHierarchyManager.updateNotificationViews();
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ clearInvocations(mListContainer);
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called only one more time
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ }
+
private class FakeListContainer implements NotificationListContainer {
final LinearLayout mLayout = new LinearLayout(mContext);
final List<View> mRows = Lists.newArrayList();
+ private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications;
@Override
public void setChildTransferInProgress(boolean childTransferInProgress) {}
@@ -263,7 +324,11 @@
}
@Override
- public void setMaxDisplayedNotifications(int maxNotifications) {}
+ public void setMaxDisplayedNotifications(int maxNotifications) {
+ if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ }
@Override
public ViewGroup getViewParentForNotification(NotificationEntry entry) {
@@ -298,5 +363,7 @@
return false;
}
+ @Override
+ public void onNotificationViewUpdateFinished() { }
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 4e416a2..1046e82 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -103,6 +103,7 @@
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
"android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
+ "android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.graphics.allocator@2.0::IAllocator",
@@ -113,7 +114,7 @@
"android.hardware.media.omx@1.0::IOmxStore",
"android.hardware.sensors@1.0::ISensors",
"android.hardware.vr@1.0::IVr",
- "android.hardware.biometrics.face@1.0::IBiometricsFace"
+ "android.system.suspend@1.0::ISystemSuspend"
);
static Watchdog sWatchdog;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a7da3ec..d3e5df5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2269,6 +2269,11 @@
return;
}
+ // Permission is already revoked, no need to do anything.
+ if (!permissionsState.hasRuntimePermission(permName, userId)) {
+ return;
+ }
+
if (permissionsState.revokeRuntimePermission(bp, userId) ==
PERMISSION_OPERATION_FAILURE) {
return;
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 9cd6b0d..c6a1867 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -49,6 +49,7 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.provider.Telephony;
import android.service.sms.FinancialSmsService;
import android.telephony.IFinancialSmsCallback;
import android.text.TextUtils;
@@ -60,6 +61,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.SmsApplication;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.CollectionUtils;
@@ -377,13 +379,16 @@
}
@Override
- public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId,
+ @Nullable String removedHolder, @Nullable String addedHolder) {
mListenerHandler.sendMessage(PooledLambda.obtainMessage(
- RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
+ RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId,
+ removedHolder, addedHolder));
}
@WorkerThread
- private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId,
+ @Nullable String removedHolder, @Nullable String addedHolder) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
if (listeners != null) {
notifyRoleHoldersChangedForListeners(listeners, roleName, userId);
@@ -394,6 +399,12 @@
if (allUsersListeners != null) {
notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId);
}
+
+ // Legacy: sms app changed broadcasts
+ if (RoleManager.ROLE_SMS.equals(roleName)) {
+ SmsApplication.broadcastSmsAppChange(getContext(), UserHandle.of(userId),
+ removedHolder, addedHolder);
+ }
}
@WorkerThread
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index c7e3fa4..6375b48 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -294,7 +294,7 @@
}
if (changed) {
- mCallback.onRoleHoldersChanged(roleName, mUserId);
+ mCallback.onRoleHoldersChanged(roleName, mUserId, null, packageName);
}
return true;
}
@@ -328,7 +328,7 @@
}
if (changed) {
- mCallback.onRoleHoldersChanged(roleName, mUserId);
+ mCallback.onRoleHoldersChanged(roleName, mUserId, packageName, null);
}
return true;
}
@@ -632,6 +632,7 @@
* @param roleName the name of the role whose holders are changed
* @param userId the user id for this role holder change
*/
- void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
+ void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId,
+ @Nullable String removedHolder, @Nullable String addedHolder);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0faea61..371a943 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1616,8 +1616,11 @@
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
+ // Making sure the client state is RESUMED after transaction completed and doing
+ // so only if activity is currently RESUMED. Otherwise, client may have extra
+ // life-cycle calls to RESUMED (and PAUSED later).
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- NewIntentItem.obtain(ar));
+ NewIntentItem.obtain(ar, mState == RESUMED));
unsent = false;
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 30e866b..6bed462 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2964,7 +2964,8 @@
}
if (next.newIntents != null) {
- transaction.addCallback(NewIntentItem.obtain(next.newIntents));
+ transaction.addCallback(
+ NewIntentItem.obtain(next.newIntents, true /* resume */));
}
// Well the app will no longer be stopped.
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c992a69..19916bc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -415,7 +415,7 @@
void sendErrorResult(String message) {
try {
- if (callerApp.hasThread()) {
+ if (callerApp != null && callerApp.hasThread()) {
callerApp.getThread().scheduleCrash(message);
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1659131..b837d9e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -140,7 +140,7 @@
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
-import static com.android.server.wm.utils.RegionUtils.forEachRect;
+import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import android.animation.AnimationHandler;
@@ -5142,7 +5142,7 @@
final int[] remainingLeftRight =
{mSystemGestureExclusionLimit, mSystemGestureExclusionLimit};
- // Traverse all windows bottom up to assemble the gesture exclusion rects.
+ // Traverse all windows top down to assemble the gesture exclusion rects.
// For each window, we only take the rects that fall within its touchable region.
forAllWindows(w -> {
if (w.cantReceiveTouchInput() || !w.isVisible()
@@ -5150,23 +5150,25 @@
|| unhandled.isEmpty()) {
return;
}
- final boolean modal =
- (w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
// Get the touchable region of the window, and intersect with where the screen is still
// touchable, i.e. touchable regions on top are not covering it yet.
- w.getTouchableRegion(touchableRegion);
+ w.getEffectiveTouchableRegion(touchableRegion);
touchableRegion.op(unhandled, Op.INTERSECT);
- rectListToRegion(w.getSystemGestureExclusion(), local);
+ if (w.isImplicitlyExcludingAllSystemGestures()) {
+ local.set(touchableRegion);
+ } else {
+ rectListToRegion(w.getSystemGestureExclusion(), local);
- // Transform to display coordinates
- local.scale(w.mGlobalScale);
- final Rect frame = w.getWindowFrames().mFrame;
- local.translate(frame.left, frame.top);
+ // Transform to display coordinates
+ local.scale(w.mGlobalScale);
+ final Rect frame = w.getWindowFrames().mFrame;
+ local.translate(frame.left, frame.top);
- // A window can only exclude system gestures where it is actually touchable
- local.op(touchableRegion, Op.INTERSECT);
+ // A window can only exclude system gestures where it is actually touchable
+ local.op(touchableRegion, Op.INTERSECT);
+ }
// Apply restriction if necessary.
if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
@@ -5225,13 +5227,13 @@
r.op(edge, Op.INTERSECT);
final int[] remaining = {limit};
- forEachRect(r, rect -> {
+ forEachRectReverse(r, rect -> {
if (remaining[0] <= 0) {
return;
}
final int height = rect.height();
if (height > remaining[0]) {
- rect.bottom = rect.top + remaining[0];
+ rect.top = rect.bottom - remaining[0];
}
remaining[0] -= height;
global.op(rect, Op.UNION);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 6127303..553b0ff 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -176,6 +176,8 @@
mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token);
mTransferTouchFromToken = null;
+ // syncInputWindows here to ensure the input channel isn't removed before the transfer.
+ mTransaction.syncInputWindows();
mTransaction.apply();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2db0131..fb57d73 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE;
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -844,6 +845,7 @@
boolean mWindowsChanged = false;
int mSystemGestureExclusionLimitDp;
+ boolean mSystemGestureExcludedByPreQStickyImmersive;
public interface WindowChangeListener {
public void windowsChanged();
@@ -1142,13 +1144,21 @@
mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ mSystemGestureExcludedByPreQStickyImmersive =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
new HandlerExecutor(mH), properties -> {
synchronized (mGlobalLock) {
final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
- if (mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+ final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
+ if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
+ || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
mSystemGestureExclusionLimitDp = exclusionLimitDp;
+ mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c6c9e1b..43ad091 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -24,6 +24,8 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.SurfaceControl.Transaction;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -155,6 +157,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
+import android.os.Build;
import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
@@ -657,6 +660,15 @@
return true;
}
+ boolean isImplicitlyExcludingAllSystemGestures() {
+ final int immersiveStickyFlags =
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ final boolean immersiveSticky =
+ (mSystemUiVisibility & immersiveStickyFlags) == immersiveStickyFlags;
+ return immersiveSticky && mWmService.mSystemGestureExcludedByPreQStickyImmersive
+ && mAppToken != null && mAppToken.mTargetSdk < Build.VERSION_CODES.Q;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
@@ -2999,6 +3011,25 @@
subtractTouchExcludeRegionIfNeeded(outRegion);
}
+ /**
+ * Get the effective touchable region in global coordinates.
+ *
+ * In contrast to {@link #getTouchableRegion}, this takes into account
+ * {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.}
+ */
+ void getEffectiveTouchableRegion(Region outRegion) {
+ final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ final DisplayContent dc = getDisplayContent();
+
+ if (modal && dc != null) {
+ outRegion.set(dc.getBounds());
+ cropRegionToStackBoundsIfNeeded(outRegion);
+ subtractTouchExcludeRegionIfNeeded(outRegion);
+ } else {
+ getTouchableRegion(outRegion);
+ }
+ }
+
private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) {
final Task task = getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
index 8cd6f88..b1b3070 100644
--- a/services/core/java/com/android/server/wm/utils/RegionUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -20,6 +20,8 @@
import android.graphics.Region;
import android.graphics.RegionIterator;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -48,14 +50,21 @@
/**
* Applies actions on each rect contained within a {@code Region}.
*
+ * Order is bottom to top, then right to left.
+ *
* @param region the given region.
* @param rectConsumer the action holder.
*/
- public static void forEachRect(Region region, Consumer<Rect> rectConsumer) {
+ public static void forEachRectReverse(Region region, Consumer<Rect> rectConsumer) {
final RegionIterator it = new RegionIterator(region);
+ final ArrayList<Rect> rects = new ArrayList<>();
final Rect rect = new Rect();
while (it.next(rect)) {
- rectConsumer.accept(rect);
+ rects.add(new Rect(rect));
}
+ // TODO: instead of creating an array and reversing it, expose the reverse iterator through
+ // JNI.
+ Collections.reverse(rects);
+ rects.forEach(rectConsumer);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index f49a575..7cd097e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -26,8 +26,13 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -789,6 +794,58 @@
}
@Test
+ public void testCalculateSystemGestureExclusion_modal() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000)));
+
+ final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal");
+ win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ win2.getAttrs().width = 10;
+ win2.getAttrs().height = 10;
+ win2.setSystemGestureExclusion(Collections.emptyList());
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+ win2.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ assertEquals(expected, dc.calculateSystemGestureExclusion());
+ }
+
+ @Test
+ public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception {
+ synchronized (mWm.mGlobalLock) {
+ mWm.mSystemGestureExcludedByPreQStickyImmersive = true;
+
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility =
+ SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ win.mAppToken.mTargetSdk = P;
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ expected.set(dc.getBounds());
+ assertEquals(expected, dc.calculateSystemGestureExclusion());
+
+ win.setHasSurface(false);
+ }
+ }
+
+ @Test
public void testOrientationChangeLogging() {
MetricsLogger mockLogger = mock(MetricsLogger.class);
Configuration oldConfig = new Configuration();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c3561f4..d034f27 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -49,6 +49,7 @@
int mRotationToReport = 0;
boolean mKeyguardShowingAndNotOccluded = false;
+ boolean mOkToAnimate = true;
private Runnable mRunnableWhenAddingSplashScreen;
@@ -222,7 +223,7 @@
@Override
public boolean okToAnimate() {
- return true;
+ return mOkToAnimate;
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 44dc24b..98f52cb 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.Manifest.permission;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
@@ -662,49 +663,69 @@
}
defaultSmsAppChanged(context);
+ }
+ }
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
- }
- if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
- // Notify the old sms app that it's no longer the default
- final Intent oldAppIntent =
- new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- final ComponentName component = new ComponentName(oldAppData.mPackageName,
- oldAppData.mSmsAppChangedReceiverClass);
- oldAppIntent.setComponent(component);
- oldAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
- }
- context.sendBroadcastAsUser(oldAppIntent, userHandle);
- }
- // Notify the new sms app that it's now the default (if the new sms app has a receiver
- // to handle the changed default sms intent).
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
- applicationData);
- }
- if (applicationData.mSmsAppChangedReceiverClass != null) {
- final Intent intent =
- new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- final ComponentName component = new ComponentName(applicationData.mPackageName,
- applicationData.mSmsAppChangedReceiverClass);
- intent.setComponent(component);
- intent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + packageName);
- }
- context.sendBroadcastAsUser(intent, userHandle);
- }
+ /**
+ * Sends broadcasts on sms app change:
+ * {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED}
+ * {@link Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ */
+ public static void broadcastSmsAppChange(Context context,
+ UserHandle userHandle, @Nullable String oldPackage, @Nullable String newPackage) {
+ Collection<SmsApplicationData> apps = getApplicationCollection(context);
- // Send an implicit broadcast for the system server.
- // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ broadcastSmsAppChange(context, userHandle,
+ getApplicationForPackage(apps, oldPackage),
+ getApplicationForPackage(apps, newPackage));
+ }
+
+ private static void broadcastSmsAppChange(Context context, UserHandle userHandle,
+ @Nullable SmsApplicationData oldAppData,
+ @Nullable SmsApplicationData applicationData) {
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
+ }
+ if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
+ // Notify the old sms app that it's no longer the default
+ final Intent oldAppIntent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ final ComponentName component = new ComponentName(oldAppData.mPackageName,
+ oldAppData.mSmsAppChangedReceiverClass);
+ oldAppIntent.setComponent(component);
+ oldAppIntent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
+ }
+ context.sendBroadcastAsUser(oldAppIntent, userHandle);
+ }
+ // Notify the new sms app that it's now the default (if the new sms app has a receiver
+ // to handle the changed default sms intent).
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
+ applicationData);
+ }
+ if (applicationData != null && applicationData.mSmsAppChangedReceiverClass != null) {
final Intent intent =
- new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
- context.sendBroadcastAsUser(intent, userHandle,
- permission.MONITOR_DEFAULT_SMS_PACKAGE);
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ final ComponentName component = new ComponentName(applicationData.mPackageName,
+ applicationData.mSmsAppChangedReceiverClass);
+ intent.setComponent(component);
+ intent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + applicationData.mPackageName);
+ }
+ context.sendBroadcastAsUser(intent, userHandle);
+ }
+ // Send an implicit broadcast for the system server.
+ // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ final Intent intent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ context.sendBroadcastAsUser(intent, userHandle,
+ permission.MONITOR_DEFAULT_SMS_PACKAGE);
+
+ if (applicationData != null) {
MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
applicationData.mPackageName);
}