Merge "Make getFrameNumber lazy" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 039d656..5df8750 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34649,6 +34649,7 @@
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public final android.os.Bundle getBrowserRootHints();
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
diff --git a/api/system-current.txt b/api/system-current.txt
index 004746c..acb699d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37304,6 +37304,7 @@
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public final android.os.Bundle getBrowserRootHints();
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
diff --git a/api/test-current.txt b/api/test-current.txt
index 11f111d..e4b11bd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34724,6 +34724,7 @@
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public final android.os.Bundle getBrowserRootHints();
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 374c4f6..ee4c2f7 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -133,4 +133,10 @@
* the focused activity.
*/
public abstract List<IBinder> getTopVisibleActivities();
+
+ /**
+ * Callback for window manager to let activity manager know that docked stack changes its
+ * minimized state.
+ */
+ public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 4c4f128..aef92cf 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -29,6 +29,7 @@
import android.content.res.ResourcesKey;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.LocaleList;
@@ -430,37 +431,44 @@
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader) {
- final ResourcesKey key = new ResourcesKey(
- resDir,
- splitResDirs,
- overlayDirs,
- libDirs,
- displayId,
- overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
- compatInfo);
- classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#createBaseActivityResources");
+ final ResourcesKey key = new ResourcesKey(
+ resDir,
+ splitResDirs,
+ overlayDirs,
+ libDirs,
+ displayId,
+ overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+ compatInfo);
+ classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
- if (DEBUG) {
- Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
- + " with key=" + key);
- }
-
- synchronized (this) {
- final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
- activityToken);
-
- if (overrideConfig != null) {
- activityResources.overrideConfig.setTo(overrideConfig);
- } else {
- activityResources.overrideConfig.setToDefaults();
+ if (DEBUG) {
+ Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+ + " with key=" + key);
}
+
+ synchronized (this) {
+ final ActivityResources activityResources =
+ getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
+ if (overrideConfig != null) {
+ activityResources.overrideConfig.setTo(overrideConfig);
+ } else {
+ activityResources.overrideConfig.setToDefaults();
+ }
+ }
+
+ // Update any existing Activity Resources references.
+ updateResourcesForActivity(activityToken, overrideConfig);
+
+ // Now request an actual Resources object.
+ return getOrCreateResources(activityToken, key, classLoader);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
-
- // Update any existing Activity Resources references.
- updateResourcesForActivity(activityToken, overrideConfig);
-
- // Now request an actual Resources object.
- return getOrCreateResources(activityToken, key, classLoader);
}
/**
@@ -490,8 +498,8 @@
}
if (activityToken != null) {
- final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
- activityToken);
+ final ActivityResources activityResources =
+ getOrCreateActivityResourcesStructLocked(activityToken);
// Clean up any dead references so they don't pile up.
ArrayUtils.unstableRemoveIf(activityResources.activityResources,
@@ -539,6 +547,7 @@
final String[] systemLocales = findSystemLocales
? AssetManager.getSystem().getLocales() : null;
final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
+
// Avoid checking for non-pseudo-locales if we already know there were some from a previous
// Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
// since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
@@ -613,16 +622,21 @@
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader) {
- final ResourcesKey key = new ResourcesKey(
- resDir,
- splitResDirs,
- overlayDirs,
- libDirs,
- displayId,
- overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
- compatInfo);
- classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
- return getOrCreateResources(activityToken, key, classLoader);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
+ final ResourcesKey key = new ResourcesKey(
+ resDir,
+ splitResDirs,
+ overlayDirs,
+ libDirs,
+ displayId,
+ overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+ compatInfo);
+ classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+ return getOrCreateResources(activityToken, key, classLoader);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
/**
@@ -636,93 +650,104 @@
*/
public void updateResourcesForActivity(@NonNull IBinder activityToken,
@Nullable Configuration overrideConfig) {
- synchronized (this) {
- final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
- activityToken);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#updateResourcesForActivity");
+ synchronized (this) {
+ final ActivityResources activityResources =
+ getOrCreateActivityResourcesStructLocked(activityToken);
- if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
- // They are the same, no work to do.
- return;
- }
-
- // Grab a copy of the old configuration so we can create the delta's of each
- // Resources object associated with this Activity.
- final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
-
- // Update the Activity's base override.
- if (overrideConfig != null) {
- activityResources.overrideConfig.setTo(overrideConfig);
- } else {
- activityResources.overrideConfig.setToDefaults();
- }
-
- if (DEBUG) {
- Throwable here = new Throwable();
- here.fillInStackTrace();
- Slog.d(TAG, "updating resources override for activity=" + activityToken
- + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
- + " to newConfig="
- + Configuration.resourceQualifierString(activityResources.overrideConfig),
- here);
- }
-
- final boolean activityHasOverrideConfig =
- !activityResources.overrideConfig.equals(Configuration.EMPTY);
-
- // Rebase each Resources associated with this Activity.
- final int refCount = activityResources.activityResources.size();
- for (int i = 0; i < refCount; i++) {
- WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
- Resources resources = weakResRef.get();
- if (resources == null) {
- continue;
+ if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
+ // They are the same, no work to do.
+ return;
}
- // Extract the ResourcesKey that was last used to create the Resources for this
- // activity.
- final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
- if (oldKey == null) {
- Slog.e(TAG, "can't find ResourcesKey for resources impl="
- + resources.getImpl());
- continue;
- }
+ // Grab a copy of the old configuration so we can create the delta's of each
+ // Resources object associated with this Activity.
+ final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
- // Build the new override configuration for this ResourcesKey.
- final Configuration rebasedOverrideConfig = new Configuration();
+ // Update the Activity's base override.
if (overrideConfig != null) {
- rebasedOverrideConfig.setTo(overrideConfig);
+ activityResources.overrideConfig.setTo(overrideConfig);
+ } else {
+ activityResources.overrideConfig.setToDefaults();
}
- if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
- // Generate a delta between the old base Activity override configuration and
- // the actual final override configuration that was used to figure out the real
- // delta this Resources object wanted.
- Configuration overrideOverrideConfig = Configuration.generateDelta(
- oldConfig, oldKey.mOverrideConfiguration);
- rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
- }
-
- // Create the new ResourcesKey with the rebased override config.
- final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
- oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
- rebasedOverrideConfig, oldKey.mCompatInfo);
-
if (DEBUG) {
- Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
- + " to newKey=" + newKey);
+ Throwable here = new Throwable();
+ here.fillInStackTrace();
+ Slog.d(TAG, "updating resources override for activity=" + activityToken
+ + " from oldConfig="
+ + Configuration.resourceQualifierString(oldConfig)
+ + " to newConfig="
+ + Configuration.resourceQualifierString(
+ activityResources.overrideConfig),
+ here);
}
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
- if (resourcesImpl == null) {
- resourcesImpl = createResourcesImpl(newKey);
- mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
- }
+ final boolean activityHasOverrideConfig =
+ !activityResources.overrideConfig.equals(Configuration.EMPTY);
- if (resourcesImpl != resources.getImpl()) {
- // Set the ResourcesImpl, updating it for all users of this Resources object.
- resources.setImpl(resourcesImpl);
+ // Rebase each Resources associated with this Activity.
+ final int refCount = activityResources.activityResources.size();
+ for (int i = 0; i < refCount; i++) {
+ WeakReference<Resources> weakResRef = activityResources.activityResources.get(
+ i);
+ Resources resources = weakResRef.get();
+ if (resources == null) {
+ continue;
+ }
+
+ // Extract the ResourcesKey that was last used to create the Resources for this
+ // activity.
+ final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+ if (oldKey == null) {
+ Slog.e(TAG, "can't find ResourcesKey for resources impl="
+ + resources.getImpl());
+ continue;
+ }
+
+ // Build the new override configuration for this ResourcesKey.
+ final Configuration rebasedOverrideConfig = new Configuration();
+ if (overrideConfig != null) {
+ rebasedOverrideConfig.setTo(overrideConfig);
+ }
+
+ if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
+ // Generate a delta between the old base Activity override configuration and
+ // the actual final override configuration that was used to figure out the
+ // real delta this Resources object wanted.
+ Configuration overrideOverrideConfig = Configuration.generateDelta(
+ oldConfig, oldKey.mOverrideConfiguration);
+ rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ }
+
+ // Create the new ResourcesKey with the rebased override config.
+ final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
+ oldKey.mSplitResDirs,
+ oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
+ rebasedOverrideConfig, oldKey.mCompatInfo);
+
+ if (DEBUG) {
+ Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+ + " to newKey=" + newKey);
+ }
+
+ ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
+ if (resourcesImpl == null) {
+ resourcesImpl = createResourcesImpl(newKey);
+ mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+ }
+
+ if (resourcesImpl != resources.getImpl()) {
+ // Set the ResourcesImpl, updating it for all users of this Resources
+ // object.
+ resources.setImpl(resourcesImpl);
+ }
}
}
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
@@ -745,86 +770,95 @@
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
- if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
- + mResConfiguration.seq + ", newSeq=" + config.seq);
- return false;
- }
- int changes = mResConfiguration.updateFrom(config);
- // Things might have changed in display manager, so clear the cached displays.
- mDisplays.clear();
- DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyConfigurationToResourcesLocked");
- if (compat != null && (mResCompatibilityInfo == null ||
- !mResCompatibilityInfo.equals(compat))) {
- mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- }
+ if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+ if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ + mResConfiguration.seq + ", newSeq=" + config.seq);
+ return false;
+ }
+ int changes = mResConfiguration.updateFrom(config);
+ // Things might have changed in display manager, so clear the cached displays.
+ mDisplays.clear();
+ DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
- Configuration localeAdjustedConfig = config;
- final LocaleList configLocales = config.getLocales();
- if (!configLocales.isEmpty()) {
- setDefaultLocalesLocked(configLocales);
- final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
- if (adjustedLocales != configLocales) { // has the same result as .equals() in this case
- // The first locale in the list was not chosen. So we create a modified
- // configuration with the adjusted locales (which moves the chosen locale to the
- // front).
- localeAdjustedConfig = new Configuration();
- localeAdjustedConfig.setTo(config);
- localeAdjustedConfig.setLocales(adjustedLocales);
- // Also adjust the locale list in mResConfiguration, so that the Resources created
- // later would have the same locale list.
- if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
- mResConfiguration.setLocales(adjustedLocales);
- changes |= ActivityInfo.CONFIG_LOCALE;
+ if (compat != null && (mResCompatibilityInfo == null ||
+ !mResCompatibilityInfo.equals(compat))) {
+ mResCompatibilityInfo = compat;
+ changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+ | ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ }
+
+ Configuration localeAdjustedConfig = config;
+ final LocaleList configLocales = config.getLocales();
+ if (!configLocales.isEmpty()) {
+ setDefaultLocalesLocked(configLocales);
+ final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
+ if (adjustedLocales
+ != configLocales) { // has the same result as .equals() in this case
+ // The first locale in the list was not chosen. So we create a modified
+ // configuration with the adjusted locales (which moves the chosen locale to the
+ // front).
+ localeAdjustedConfig = new Configuration();
+ localeAdjustedConfig.setTo(config);
+ localeAdjustedConfig.setLocales(adjustedLocales);
+ // Also adjust the locale list in mResConfiguration, so that the Resources
+ // created later would have the same locale list.
+ if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
+ mResConfiguration.setLocales(adjustedLocales);
+ changes |= ActivityInfo.CONFIG_LOCALE;
+ }
}
}
- }
- Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics, compat);
+ Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
+ compat);
- ApplicationPackageManager.configurationChanged();
- //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+ ApplicationPackageManager.configurationChanged();
+ //Slog.i(TAG, "Configuration changed in " + currentPackageName());
- Configuration tmpConfig = null;
+ Configuration tmpConfig = null;
- for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
- ResourcesKey key = mResourceImpls.keyAt(i);
- ResourcesImpl r = mResourceImpls.valueAt(i).get();
- if (r != null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
- + r + " config to: " + localeAdjustedConfig);
- int displayId = key.mDisplayId;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = defaultDisplayMetrics;
- final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfiguration) {
- if (tmpConfig == null) {
- tmpConfig = new Configuration();
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+ ResourcesKey key = mResourceImpls.keyAt(i);
+ ResourcesImpl r = mResourceImpls.valueAt(i).get();
+ if (r != null) {
+ if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ + r + " config to: " + localeAdjustedConfig);
+ int displayId = key.mDisplayId;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = defaultDisplayMetrics;
+ final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+ if (!isDefaultDisplay || hasOverrideConfiguration) {
+ if (tmpConfig == null) {
+ tmpConfig = new Configuration();
+ }
+ tmpConfig.setTo(localeAdjustedConfig);
+ if (!isDefaultDisplay) {
+ dm = getDisplayMetrics(displayId);
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+ }
+ if (hasOverrideConfiguration) {
+ tmpConfig.updateFrom(key.mOverrideConfiguration);
+ }
+ r.updateConfiguration(tmpConfig, dm, compat);
+ } else {
+ r.updateConfiguration(localeAdjustedConfig, dm, compat);
}
- tmpConfig.setTo(localeAdjustedConfig);
- if (!isDefaultDisplay) {
- dm = getDisplayMetrics(displayId);
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
- }
- r.updateConfiguration(tmpConfig, dm, compat);
+ //Slog.i(TAG, "Updated app resources " + v.getKey()
+ // + " " + r + ": " + r.getConfiguration());
} else {
- r.updateConfiguration(localeAdjustedConfig, dm, compat);
+ //Slog.i(TAG, "Removing old resources " + v.getKey());
+ mResourceImpls.removeAt(i);
}
- //Slog.i(TAG, "Updated app resources " + v.getKey()
- // + " " + r + ": " + r.getConfiguration());
- } else {
- //Slog.i(TAG, "Removing old resources " + v.getKey());
- mResourceImpls.removeAt(i);
}
- }
- return changes != 0;
+ return changes != 0;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cd6e572..7cd13ea 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -542,7 +542,8 @@
public RttManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.WIFI_RTT_SERVICE);
IRttManager service = IRttManager.Stub.asInterface(b);
- return new RttManager(ctx.getOuterContext(), service);
+ return new RttManager(ctx.getOuterContext(), service,
+ ConnectivityThread.getInstanceLooper());
}});
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ade2248..787974d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2181,7 +2181,7 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device implements {@link #FEATURE_VR_MODE} but additionally meets all CTS requirements
+ * The device implements {@link #FEATURE_VR_MODE} but additionally meets all CDD requirements
* to be certified as a "VR Ready" device, which guarantees that the device is capable of
* delivering consistent performance at a high framerate over an extended period of time for
* typical VR application CPU/GPU workloads with a minimal number of frame drops, implements
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index dd73e53..7d74a12 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -437,6 +437,14 @@
return filePath.startsWith(dirPath);
}
+ public static boolean deleteContentsAndDir(File dir) {
+ if (deleteContents(dir)) {
+ return dir.delete();
+ } else {
+ return false;
+ }
+ }
+
public static boolean deleteContents(File dir) {
File[] files = dir.listFiles();
boolean success = true;
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index c21c65a..3915b02 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1325,6 +1325,24 @@
}
@Override
+ public void destroyUserStorage(String volumeUuid, int userId, int flags)
+ throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volumeUuid);
+ _data.writeInt(userId);
+ _data.writeInt(flags);
+ mRemote.transact(Stub.TRANSACTION_destroyUserStorage, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -1465,6 +1483,7 @@
static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
+ static final int TRANSACTION_destroyUserStorage = IBinder.FIRST_CALL_TRANSACTION + 67;
static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
@@ -2096,6 +2115,15 @@
reply.writeNoException();
return true;
}
+ case TRANSACTION_destroyUserStorage: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = data.readString();
+ int userId = data.readInt();
+ int _flags = data.readInt();
+ destroyUserStorage(volumeUuid, userId, _flags);
+ reply.writeNoException();
+ return true;
+ }
case TRANSACTION_mountAppFuse: {
data.enforceInterface(DESCRIPTOR);
String name = data.readString();
@@ -2434,6 +2462,7 @@
public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
int flags) throws RemoteException;
+ public void destroyUserStorage(String volumeUuid, int userId, int flags) throws RemoteException;
public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0a8fdd9..f68e227 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1053,6 +1053,15 @@
}
/** {@hide} */
+ public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+ try {
+ mMountService.destroyUserStorage(volumeUuid, userId, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@hide} */
public boolean isUserKeyUnlocked(int userId) {
try {
return mMountService.isUserKeyUnlocked(userId);
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 2481e04..415c291 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.CanvasProperty;
@@ -184,7 +185,7 @@
* any references to the functor, just that the reference from this specific
* canvas's display list has been released.
*/
- public void drawGLFunctor2(long drawGLFunctor, Runnable releasedCallback) {
+ public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a0f5142..7f92777 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -417,6 +417,21 @@
public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
/**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it. This flag is set to true
+ * even if the event did not directly pass through the obscured area.
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ *
+ * Unlike FLAG_WINDOW_IS_OBSCURED, this is actually true.
+ * @hide
+ */
+ public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
+
+ /**
* Private flag that indicates when the system has detected that this motion event
* may be inconsistent with respect to the sequence of previously delivered motion events,
* such as when a pointer move event is sent but the pointer is not down.
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 738aaca..b1598e7 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -270,15 +270,12 @@
mLastXOffset = xOffset;
mLastYOffset = yOffset;
- // Only clip the content to the bounds if we are not fullscreen. In the other case, we
- // actually need to draw outside these.
- if (mResizeMode == RESIZE_MODE_FREEFORM) {
- mRenderer.setContentDrawBounds(
- mLastXOffset,
- mLastYOffset,
- mLastXOffset + mLastContentWidth,
- mLastYOffset + mLastCaptionHeight + mLastContentHeight);
- }
+ // Inform the renderer of the content's new bounds
+ mRenderer.setContentDrawBounds(
+ mLastXOffset,
+ mLastYOffset,
+ mLastXOffset + mLastContentWidth,
+ mLastYOffset + mLastCaptionHeight + mLastContentHeight);
// If this was the first call and redrawLocked got already called prior
// to us, we should re-issue a redrawLocked now.
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 4f17c39..eac9f64 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -17,22 +17,27 @@
package com.android.internal.policy;
import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
import android.view.ContextThemeWrapper;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
/**
* Context for decor views which can be seeded with pure application context and not depend on the
- * activity, but still provide some of the facilities that Activity has, e.g. themes.
+ * activity, but still provide some of the facilities that Activity has,
+ * e.g. themes, activity-based resources, etc.
*
* @hide
*/
class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
+ private Resources mActivityResources;
- public DecorContext(Context context) {
+ public DecorContext(Context context, Resources activityResources) {
super(context, null);
+ mActivityResources = activityResources;
}
void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -52,4 +57,14 @@
}
return super.getSystemService(name);
}
+
+ @Override
+ public Resources getResources() {
+ return mActivityResources;
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ return mActivityResources.getAssets();
+ }
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index fe63267..151c530 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2297,7 +2297,7 @@
if (applicationContext == null) {
context = getContext();
} else {
- context = new DecorContext(applicationContext);
+ context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index cadfd3d..d6f9db5 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -105,8 +105,10 @@
jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
Functor* functor = reinterpret_cast<Functor*>(functorPtr);
- sp<GlFunctorReleasedCallbackBridge> bridge(new GlFunctorReleasedCallbackBridge(
- env, releasedCallback));
+ sp<GlFunctorReleasedCallbackBridge> bridge;
+ if (releasedCallback) {
+ bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback);
+ }
canvas->callDrawGLFunction(functor, bridge.get());
}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 6913f43..715c875 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -34,9 +34,7 @@
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Timers.h>
-#ifdef __ANDROID__
-#include <cutils/trace.h>
-#endif
+#include <utils/Trace.h>
#include <assert.h>
#include <dirent.h>
@@ -54,14 +52,6 @@
_rc; })
#endif
-#ifdef __ANDROID__
-#define MY_TRACE_BEGIN(x) ATRACE_BEGIN(x)
-#define MY_TRACE_END() ATRACE_END()
-#else
-#define MY_TRACE_BEGIN(x)
-#define MY_TRACE_END()
-#endif
-
using namespace android;
static const bool kIsDebug = false;
@@ -623,7 +613,7 @@
ResTable* sharedRes = NULL;
bool shared = true;
bool onlyEmptyResources = true;
- MY_TRACE_BEGIN(ap.path.string());
+ ATRACE_NAME(ap.path.string());
Asset* idmap = openIdmapLocked(ap);
size_t nextEntryIdx = mResources->getTableCount();
ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
@@ -703,8 +693,6 @@
if (idmap != NULL) {
delete idmap;
}
- MY_TRACE_END();
-
return onlyEmptyResources;
}
@@ -752,6 +740,7 @@
void AssetManager::updateResourceParamsLocked() const
{
+ ATRACE_CALL();
ResTable* res = mResources;
if (!res) {
return;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1ccc59a..15cb684 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <string.h>
+#include <algorithm>
#include <limits>
#include <memory>
#include <type_traits>
@@ -5810,6 +5811,10 @@
return NULL;
}
+static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
+ return a.compare(b) < 0;
+}
+
void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
bool ignoreAndroidPackage, bool includeSystemConfigs) const {
const size_t packageCount = mPackageGroups.size();
@@ -5840,17 +5845,11 @@
ResTable_config cfg;
memset(&cfg, 0, sizeof(ResTable_config));
cfg.copyFromDtoH(config->config);
- // only insert unique
- const size_t N = configs->size();
- size_t n;
- for (n = 0; n < N; n++) {
- if (0 == (*configs)[n].compare(cfg)) {
- break;
- }
- }
- // if we didn't find it
- if (n == N) {
- configs->add(cfg);
+
+ auto iter = std::lower_bound(configs->begin(), configs->end(), cfg,
+ compareResTableConfig);
+ if (iter == configs->end() || iter->compare(cfg) != 0) {
+ configs->insertAt(cfg, std::distance(configs->begin(), iter));
}
}
}
@@ -5858,6 +5857,10 @@
}
}
+static bool compareString8AndCString(const String8& str, const char* cStr) {
+ return strcmp(str.string(), cStr) < 0;
+}
+
void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
{
Vector<ResTable_config> configs;
@@ -5872,15 +5875,11 @@
char locale[RESTABLE_MAX_LOCALE_LEN];
for (size_t i=0; i<I; i++) {
configs[i].getBcp47Locale(locale);
- const size_t J = locales->size();
- size_t j;
- for (j=0; j<J; j++) {
- if (0 == strcmp(locale, (*locales)[j].string())) {
- break;
- }
- }
- if (j == J) {
- locales->add(String8(locale));
+
+ auto iter = std::lower_bound(locales->begin(), locales->end(), locale,
+ compareString8AndCString);
+ if (iter == locales->end() || strcmp(iter->string(), locale) != 0) {
+ locales->insertAt(String8(locale), std::distance(locales->begin(), iter));
}
}
}
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 7cd7fb5..b8b4625 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -39,8 +39,20 @@
*/
#include "data/basic/basic_arsc.h"
+/**
+ * Include a binary library resource table.
+ *
+ * Package: com.android.test.basic
+ */
#include "data/lib/lib_arsc.h"
+/**
+ * Include a system resource table.
+ *
+ * Package: android
+ */
+#include "data/system/system_arsc.h"
+
TEST(ResTableTest, shouldLoadSuccessfully) {
ResTable table;
ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -324,4 +336,25 @@
ASSERT_EQ(uint32_t(600), val.data);
}
+TEST(ResTableTest, GetConfigurationsReturnsUniqueList) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable_config configSv;
+ memset(&configSv, 0, sizeof(configSv));
+ configSv.language[0] = 's';
+ configSv.language[1] = 'v';
+
+ Vector<ResTable_config> configs;
+ table.getConfigurations(&configs);
+
+ EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
+
+ Vector<String8> locales;
+ table.getLocales(&locales);
+
+ EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
+}
+
} // namespace
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ac80d88..ff9be16 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -21,7 +21,7 @@
enum { MAY_NOT_BE_BAG = false };
static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
- return memcmp(&a, &b, sizeof(a)) == 0;
+ return a.compare(b) == 0;
}
static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index 27f25fe..6a31fb8 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -33,6 +33,12 @@
};
}
+namespace integer {
+ enum {
+ number = 0x01030000, // sv
+ };
+}
+
} // namespace R
} // namespace android
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
new file mode 100644
index 0000000..b97bdb6
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <public type="integer" name="number" id="0x01030000" />
+ <integer name="number">1</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
index 215ecae..b0dab6b 100644
--- a/libs/androidfw/tests/data/system/system_arsc.h
+++ b/libs/androidfw/tests/data/system/system_arsc.h
@@ -1,8 +1,8 @@
unsigned char system_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, 0x03, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -25,26 +25,33 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
- 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
- 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x78, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00,
+ 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x5e, 0x00,
+ 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x70, 0x00,
+ 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x84, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
- 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
- 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -55,15 +62,27 @@
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
- 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
- 0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01,
+ 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-unsigned int system_arsc_len = 792;
+unsigned int system_arsc_len = 1016;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 6fc74a5..502f027 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -31,18 +31,16 @@
namespace android {
namespace uirenderer {
-FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes,
- const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
- : mCanvasState(*this)
+ const LightGeometry& lightGeometry, Caches& caches)
+ : mStdAllocator(mAllocator)
+ , mLayerBuilders(mStdAllocator)
+ , mLayerStack(mStdAllocator)
+ , mCanvasState(*this)
, mCaches(caches)
, mLightRadius(lightGeometry.radius)
- , mDrawFbo0(!nodes.empty()) {
- ATRACE_NAME("prepare drawing commands");
-
- mLayerBuilders.reserve(layers.entries().size());
- mLayerStack.reserve(layers.entries().size());
+ , mDrawFbo0(true) {
// Prepare to defer Fbo0
auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
@@ -51,7 +49,31 @@
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
lightGeometry.center);
+}
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers,
+ const LightGeometry& lightGeometry, Caches& caches)
+ : mStdAllocator(mAllocator)
+ , mLayerBuilders(mStdAllocator)
+ , mLayerStack(mStdAllocator)
+ , mCanvasState(*this)
+ , mCaches(caches)
+ , mLightRadius(lightGeometry.radius)
+ , mDrawFbo0(false) {
+ // TODO: remove, with each layer on its own save stack
+
+ // Prepare to defer Fbo0 (which will be empty)
+ auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1));
+ mLayerBuilders.push_back(fbo0);
+ mLayerStack.push_back(0);
+ mCanvasState.initializeSaveStack(1, 1,
+ 0, 0, 1, 1,
+ lightGeometry.center);
+
+ deferLayers(layers);
+}
+
+void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
// updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
for (int i = layers.entries().size() - 1; i >= 0; i--) {
@@ -76,10 +98,45 @@
restoreForLayer();
}
}
+}
+void FrameBuilder::deferRenderNode(RenderNode& renderNode) {
+ renderNode.computeOrdering();
+
+ mCanvasState.save(SaveFlags::MatrixClip);
+ deferNodePropsAndOps(renderNode);
+ mCanvasState.restore();
+}
+
+void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) {
+ renderNode.computeOrdering();
+
+ mCanvasState.save(SaveFlags::MatrixClip);
+ mCanvasState.translate(tx, ty);
+ mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+ SkRegion::kIntersect_Op);
+ deferNodePropsAndOps(renderNode);
+ mCanvasState.restore();
+}
+
+static Rect nodeBounds(RenderNode& node) {
+ auto& props = node.properties();
+ return Rect(props.getLeft(), props.getTop(),
+ props.getRight(), props.getBottom());
+}
+
+void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
+ const Rect& contentDrawBounds) {
+ if (nodes.size() < 1) return;
+ if (nodes.size() == 1) {
+ if (!nodes[0]->nothingToDraw()) {
+ deferRenderNode(*nodes[0]);
+ }
+ return;
+ }
// It there are multiple render nodes, they are laid out as follows:
// #0 - backdrop (content + caption)
- // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+ // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
// #2 - additional overlay nodes
// Usually the backdrop cannot be seen since it will be entirely covered by the content. While
// resizing however it might become partially visible. The following render loop will crop the
@@ -88,45 +145,52 @@
//
// Additional nodes will be drawn on top with no particular clipping semantics.
- // The bounds of the backdrop against which the content should be clipped.
- Rect backdropBounds = contentDrawBounds;
// Usually the contents bounds should be mContentDrawBounds - however - we will
// move it towards the fixed edge to give it a more stable appearance (for the moment).
// If there is no content bounds we ignore the layering as stated above and start with 2.
- int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
- for (const sp<RenderNode>& node : nodes) {
- if (node->nothingToDraw()) continue;
- node->computeOrdering();
- int count = mCanvasState.save(SaveFlags::MatrixClip);
+ // Backdrop bounds in render target space
+ const Rect backdrop = nodeBounds(*nodes[0]);
- if (layer == 0) {
- const RenderProperties& properties = node->properties();
- Rect targetBounds(properties.getLeft(), properties.getTop(),
- properties.getRight(), properties.getBottom());
- // Move the content bounds towards the fixed corner of the backdrop.
- const int x = targetBounds.left;
- const int y = targetBounds.top;
- // Remember the intersection of the target bounds and the intersection bounds against
- // which we have to crop the content.
- backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
- backdropBounds.doIntersect(targetBounds);
- } else if (layer == 1) {
- // We shift and clip the content to match its final location in the window.
- const float left = contentDrawBounds.left;
- const float top = contentDrawBounds.top;
- const float dx = backdropBounds.left - left;
- const float dy = backdropBounds.top - top;
- const float width = backdropBounds.getWidth();
- const float height = backdropBounds.getHeight();
- mCanvasState.translate(dx, dy);
- // It gets cropped against the bounds of the backdrop to stay inside.
- mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op);
+ // Bounds that content will fill in render target space (note content node bounds may be bigger)
+ Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
+ content.translate(backdrop.left, backdrop.top);
+ if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
+ // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
+
+ // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
+ // also fill left/top. Currently, both 2up and freeform position content at the top/left of
+ // the backdrop, so this isn't necessary.
+ if (content.right < backdrop.right) {
+ // draw backdrop to right side of content
+ deferRenderNode(0, 0, Rect(content.right, backdrop.top,
+ backdrop.right, backdrop.bottom), *nodes[0]);
}
+ if (content.bottom < backdrop.bottom) {
+ // draw backdrop to bottom of content
+ // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
+ deferRenderNode(0, 0, Rect(content.left, content.bottom,
+ content.right, backdrop.bottom), *nodes[0]);
+ }
+ }
- deferNodePropsAndOps(*node);
- mCanvasState.restoreToCount(count);
- layer++;
+ if (!backdrop.isEmpty()) {
+ // content node translation to catch up with backdrop
+ float dx = contentDrawBounds.left - backdrop.left;
+ float dy = contentDrawBounds.top - backdrop.top;
+
+ Rect contentLocalClip = backdrop;
+ contentLocalClip.translate(dx, dy);
+ deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
+ } else {
+ deferRenderNode(*nodes[1]);
+ }
+
+ // remaining overlay nodes, simply defer
+ for (size_t index = 2; index < nodes.size(); index++) {
+ if (!nodes[index]->nothingToDraw()) {
+ deferRenderNode(*nodes[index]);
+ }
}
}
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index a6fd761..b915443 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -37,8 +37,8 @@
class Rect;
/**
- * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
- * them to be rendered.
+ * Processes, optimizes, and stores rendering commands from RenderNodes and
+ * LayerUpdateQueue, building content needed to render a frame.
*
* Resolves final drawing state for each operation (including clip, alpha and matrix), and then
* reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
@@ -60,21 +60,21 @@
float radius;
};
- // TODO: remove
- FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+ FrameBuilder(const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes,
- const LightGeometry& lightGeometry,
- Caches& caches)
- : FrameBuilder(layers, clip, viewportWidth, viewportHeight,
- nodes, lightGeometry, Rect(), caches) {}
+ const LightGeometry& lightGeometry, Caches& caches);
- FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
- uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes,
- const LightGeometry& lightGeometry,
- const Rect &contentDrawBounds,
- Caches& caches);
+ FrameBuilder(const LayerUpdateQueue& layerUpdateQueue,
+ const LightGeometry& lightGeometry, Caches& caches);
+
+ void deferLayers(const LayerUpdateQueue& layers);
+
+ void deferRenderNode(RenderNode& renderNode);
+
+ void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode);
+
+ void deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes,
+ const Rect& contentDrawBounds);
virtual ~FrameBuilder() {}
@@ -223,8 +223,12 @@
MAP_DEFERRABLE_OPS(X)
#undef X
+ // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
+ LinearAllocator mAllocator;
+ LinearStdAllocator<void*> mStdAllocator;
+
// List of every deferred layer's render state. Replayed in reverse order to render a frame.
- std::vector<LayerBuilder*> mLayerBuilders;
+ LsaVector<LayerBuilder*> mLayerBuilders;
/*
* Stack of indices within mLayerBuilders representing currently active layers. If drawing
@@ -238,7 +242,7 @@
* won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
* ops added to it.
*/
- std::vector<size_t> mLayerStack;
+ LsaVector<size_t> mLayerStack;
CanvasState mCanvasState;
@@ -246,9 +250,6 @@
float mLightRadius;
- // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
- LinearAllocator mAllocator;
-
const bool mDrawFbo0;
};
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index eea11bf..3000777 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -244,7 +244,8 @@
if (CC_UNLIKELY(activeUnclippedSaveLayers.empty()
&& bakedState->computedState.opaqueOverClippedBounds
- && bakedState->computedState.clippedBounds.contains(repaintRect))) {
+ && bakedState->computedState.clippedBounds.contains(repaintRect)
+ && !Properties::debugOverdraw)) {
// discard all deferred drawing ops, since new one will occlude them
clear();
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index df5c8d9..e6399d4 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -354,9 +354,13 @@
#if HWUI_NEW_OPS
auto& caches = Caches::getInstance();
- FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
- mRenderNodes, mLightGeometry, mContentDrawBounds, caches);
+ FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches);
+
+ frameBuilder.deferLayers(mLayerUpdateQueue);
mLayerUpdateQueue.clear();
+
+ frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds);
+
BakedOpRenderer renderer(caches, mRenderThread.renderState(),
mOpaque, mLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -622,8 +626,7 @@
#if HWUI_NEW_OPS
static const std::vector< sp<RenderNode> > emptyNodeList;
auto& caches = Caches::getInstance();
- FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
- emptyNodeList, mLightGeometry, mContentDrawBounds, caches);
+ FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches);
mLayerUpdateQueue.clear();
BakedOpRenderer renderer(caches, mRenderThread.renderState(),
mOpaque, mLightInfo);
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5f4ebc0..dbaefa4 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -171,11 +171,9 @@
syncHierarchyPropertiesAndDisplayListImpl(node.get());
}
- static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- std::vector<sp<RenderNode>> vec;
- vec.emplace_back(node);
- return vec;
+ static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) {
+ syncHierarchyPropertiesAndDisplayList(node);
+ return node;
}
typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 0aef620..84ef9c2 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -35,11 +35,10 @@
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::test;
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
-static std::vector<sp<RenderNode>> createTestNodeList() {
+static sp<RenderNode> createTestNode() {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
@@ -56,31 +55,33 @@
canvas.restore();
});
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- std::vector<sp<RenderNode>> vec;
- vec.emplace_back(node);
- return vec;
+ return node;
}
void BM_FrameBuilder_defer(benchmark::State& state) {
- auto nodes = createTestNodeList();
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
- nodes, sLightGeometry, Caches::getInstance());
- benchmark::DoNotOptimize(&frameBuilder);
- }
+ TestUtils::runOnRenderThread([&state](RenderThread& thread) {
+ auto node = createTestNode();
+ while (state.KeepRunning()) {
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*node);
+ benchmark::DoNotOptimize(&frameBuilder);
+ }
+ });
}
BENCHMARK(BM_FrameBuilder_defer);
void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- auto nodes = createTestNodeList();
+ auto node = createTestNode();
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
while (state.KeepRunning()) {
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
- nodes, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+ sLightGeometry, caches);
+ frameBuilder.deferRenderNode(*node);
BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -90,7 +91,7 @@
}
BENCHMARK(BM_FrameBuilder_deferAndRender);
-static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) {
+static sp<RenderNode> getSyncedSceneNode(const char* sceneName) {
gDisplay = getBuiltInDisplay(); // switch to real display if present
TestContext testContext;
@@ -103,9 +104,7 @@
});
TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
- std::vector<sp<RenderNode>> nodes;
- nodes.emplace_back(rootNode);
- return nodes;
+ return rootNode;
}
static auto SCENES = {
@@ -116,11 +115,12 @@
TestUtils::runOnRenderThread([&state](RenderThread& thread) {
const char* sceneName = *(SCENES.begin() + state.range_x());
state.SetLabel(sceneName);
- auto nodes = getSyncedSceneNodes(sceneName);
+ auto node = getSyncedSceneNode(sceneName);
while (state.KeepRunning()) {
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
- nodes, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h),
+ gDisplay.w, gDisplay.h,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*node);
benchmark::DoNotOptimize(&frameBuilder);
}
});
@@ -131,15 +131,16 @@
TestUtils::runOnRenderThread([&state](RenderThread& thread) {
const char* sceneName = *(SCENES.begin() + state.range_x());
state.SetLabel(sceneName);
- auto nodes = getSyncedSceneNodes(sceneName);
+ auto node = getSyncedSceneNode(sceneName);
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
while (state.KeepRunning()) {
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
- nodes, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h),
+ gDisplay.w, gDisplay.h,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*node);
BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index ebc1c80..0f16b15 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -29,11 +29,8 @@
namespace android {
namespace uirenderer {
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
-const std::vector< sp<RenderNode> > sEmptyNodeList;
const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
-
/**
* Virtual class implemented by each test to redirect static operation / state transitions to
* virtual methods.
@@ -136,8 +133,10 @@
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SimpleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
@@ -162,8 +161,10 @@
strokedPaint.setStrokeWidth(10);
canvas.drawPoint(50, 50, strokedPaint);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SimpleStrokeTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
@@ -177,8 +178,9 @@
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
FailRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
@@ -211,15 +213,111 @@
}
canvas.restore();
});
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SimpleBatchingTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
<< "Expect number of ops = 2 * loop count";
}
+RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
+ class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
+ EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
+ state.computedState.clipSideFlags);
+ }
+ };
+
+ auto node = TestUtils::createNode(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ });
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
+ *TestUtils::getSyncedNode(node));
+
+ DeferRenderNodeTranslateClipTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
+ class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ const Rect& clippedBounds = state.computedState.clippedBounds;
+ Matrix4 expected;
+ switch (mIndex++) {
+ case 0:
+ // background - left side
+ EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
+ expected.loadTranslate(100, 100, 0);
+ break;
+ case 1:
+ // background - top side
+ EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
+ expected.loadTranslate(100, 100, 0);
+ break;
+ case 2:
+ // content
+ EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
+ expected.loadTranslate(-50, -50, 0);
+ break;
+ case 3:
+ // overlay
+ EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
+ break;
+ default:
+ ADD_FAILURE() << "Too many rects observed";
+ }
+ EXPECT_EQ(expected, state.computedState.transform);
+ }
+ };
+
+ std::vector<sp<RenderNode>> nodes;
+ SkPaint transparentPaint;
+ transparentPaint.setAlpha(128);
+
+ // backdrop
+ nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400
+ [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 600, 400, transparentPaint);
+ }));
+
+ // content
+ Rect contentDrawBounds(150, 150, 650, 450); // 500x300
+ nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+ [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 800, 600, transparentPaint);
+ }));
+
+ // overlay
+ nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+ [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 800, 200, transparentPaint);
+ }));
+
+ for (auto& node : nodes) {
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ }
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
+
+ DeferRenderNodeSceneTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
class EmptyNoFbo0TestRenderer : public TestRendererBase {
public:
@@ -231,9 +329,9 @@
}
};
- // Pass empty node list, so no work is enqueued for Fbo0
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- sEmptyNodeList, sLightGeometry, Caches::getInstance());
+ // Use layer update constructor, so no work is enqueued for Fbo0
+ LayerUpdateQueue emptyLayerUpdateQueue;
+ FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
EmptyNoFbo0TestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
@@ -252,11 +350,12 @@
[](RenderProperties& props, RecordingCanvas& canvas) {
// no drawn content
});
- auto syncedNodeList = TestUtils::createSyncedNodeList(node);
- // Draw, but pass empty node list, so no work is done for primary frame
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- syncedNodeList, sLightGeometry, Caches::getInstance());
+ // Draw, but pass node without draw content, so no work is done for primary frame
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
EmptyWithFbo0TestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
@@ -281,9 +380,9 @@
// Damage (and therefore clip) is same as last draw, subset of renderable area.
// This means last op occludes other contents, and they'll be rejected to avoid overdraw.
- SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
<< "Recording must not have rejected ops, in order for this test to be valid";
@@ -324,9 +423,9 @@
canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
});
-
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
<< "Recording must not have rejected ops, in order for this test to be valid";
@@ -369,8 +468,10 @@
canvas.drawBitmap(bitmap, 40, 70, nullptr);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
ClippedMergingTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
@@ -397,8 +498,10 @@
TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
TextMergingTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
@@ -428,8 +531,11 @@
TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
}
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
TextStrikethroughTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
@@ -485,8 +591,9 @@
TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
}
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
TextStyleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
@@ -516,8 +623,11 @@
canvas.drawLayer(layerUpdater.get());
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
TextureLayerClipLocalMatrixTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
@@ -546,8 +656,10 @@
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
TextureLayerCombineMatricesTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
@@ -562,8 +674,11 @@
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawLayer(layerUpdater.get());
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
FailRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
@@ -584,9 +699,10 @@
canvas.callDrawGLFunction(&noopFunctor, nullptr);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(scrolledFunctorView),
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
+
FunctorTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
@@ -608,9 +724,10 @@
canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(unclippedColorView),
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
+
ColorTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
@@ -654,8 +771,10 @@
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
RenderNodeTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
@@ -678,9 +797,11 @@
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
- SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
- 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ // clip to small area, should see in receiver
+ FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
ClippedTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
@@ -725,8 +846,11 @@
canvas.drawRect(10, 10, 190, 190, SkPaint());
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SaveLayerSimpleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
@@ -806,8 +930,10 @@
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SaveLayerNestedTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(12, renderer.getIndex());
@@ -826,8 +952,10 @@
canvas.restore();
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
FailRenderer renderer;
// should see no ops, even within the layer, since the layer should be rejected
@@ -869,8 +997,11 @@
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SaveLayerUnclippedSimpleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
@@ -923,8 +1054,11 @@
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restoreToCount(restoreTo);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SaveLayerUnclippedMergedClearsTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex())
@@ -964,8 +1098,10 @@
});
// draw with partial screen dirty, and assert we see that rect later
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SaveLayerUnclippedClearClipTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
@@ -981,8 +1117,10 @@
});
// draw with partial screen dirty that doesn't intersect with savelayer
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
FailRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
@@ -1046,8 +1184,11 @@
canvas.restore();
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
SaveLayerUnclippedComplexTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(13, renderer.getIndex());
@@ -1098,14 +1239,17 @@
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
*layerHandle = &layer;
- auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+ auto syncedNode = TestUtils::getSyncedNode(node);
// only enqueue partial damage
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
- FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- syncedNodeList, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferLayers(layerUpdateQueue);
+ frameBuilder.deferRenderNode(*syncedNode);
+
HwLayerSimpleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
@@ -1202,14 +1346,17 @@
OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
*(parent->getLayerHandle()) = &parentLayer;
- auto syncedList = TestUtils::createSyncedNodeList(parent);
+ auto syncedNode = TestUtils::getSyncedNode(parent);
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
- FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- syncedList, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferLayers(layerUpdateQueue);
+ frameBuilder.deferRenderNode(*syncedNode);
+
HwLayerComplexTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(14, renderer.getIndex());
@@ -1260,15 +1407,14 @@
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
*layerHandle = &layer;
- auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
// only enqueue partial damage
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
// Draw, but pass empty node list, so no work is done for primary frame
- FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
- sEmptyNodeList, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
BuildLayerTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(3, renderer.getIndex());
@@ -1315,8 +1461,10 @@
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
- TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ZReorderTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
@@ -1406,8 +1554,10 @@
canvas.restore();
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
- TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ProjectionReorderTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(3, renderer.getIndex());
@@ -1486,11 +1636,16 @@
layer.setWindowTransform(windowTransform);
*layerHandle = &layer;
- auto syncedList = TestUtils::createSyncedNodeList(parent);
+ auto syncedNode = TestUtils::getSyncedNode(parent);
+
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
- FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
- syncedList, sLightGeometry, Caches::getInstance());
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferLayers(layerUpdateQueue);
+ frameBuilder.deferRenderNode(*syncedNode);
+
ProjectionHwLayerTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
@@ -1545,8 +1700,10 @@
canvas.drawRenderNode(child.get());
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
- TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ProjectionChildScrollTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
@@ -1588,8 +1745,10 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ShadowTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
@@ -1632,9 +1791,10 @@
canvas.restoreToCount(count);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(parent),
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
(FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ShadowSaveLayerTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
@@ -1681,12 +1841,15 @@
layer.setWindowTransform(windowTransform);
*layerHandle = &layer;
- auto syncedList = TestUtils::createSyncedNodeList(parent);
+ auto syncedNode = TestUtils::getSyncedNode(parent);
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
- FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- syncedList,
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
(FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance());
+ frameBuilder.deferLayers(layerUpdateQueue);
+ frameBuilder.deferRenderNode(*syncedNode);
+
ShadowHwLayerTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
@@ -1713,10 +1876,10 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
});
-
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(parent),
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
(FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ShadowLayeringTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
@@ -1743,9 +1906,10 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
- TestUtils::createSyncedNodeList(parent),
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
(FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
+
ShadowClippingTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
@@ -1772,8 +1936,10 @@
canvas.drawRect(0, 0, 100, 100, paint);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
PropertyTestRenderer renderer(opValidateCallback);
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
@@ -1915,10 +2081,12 @@
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 10000, 10000, paint);
});
- auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
+ auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- nodes, sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*syncedNode);
+
SaveLayerAlphaClipTestRenderer renderer(outObservedData);
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
@@ -1991,8 +2159,10 @@
canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
});
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
- TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
ClipReplaceTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index e2fc376..6148b33 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -26,7 +26,6 @@
using namespace android;
using namespace android::uirenderer;
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
@@ -43,8 +42,9 @@
RenderState& renderState = renderThread.renderState();
Caches& caches = Caches::getInstance();
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
- TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometery, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
@@ -59,8 +59,9 @@
RenderState& renderState = renderThread.renderState();
Caches& caches = Caches::getInstance();
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometery, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 2cd9872..baa6b0f 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -135,7 +135,7 @@
mContext = context;
mServiceComponent = serviceComponent;
mCallback = callback;
- mRootHints = rootHints;
+ mRootHints = rootHints == null ? null : new Bundle(rootHints);
}
/**
@@ -444,7 +444,7 @@
}
};
try {
- mServiceBinder.getMediaItem(mediaId, receiver);
+ mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
} catch (RemoteException e) {
Log.i(TAG, "Remote error getting media item.");
mHandler.post(new Runnable() {
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index eef5a7c..6b6a561 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -20,5 +20,5 @@
void addSubscription(String uri, in IBinder token, in Bundle options,
IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
- void getMediaItem(String uri, in ResultReceiver cb);
+ void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index ddc0e88..4b88926 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -97,6 +97,7 @@
private @interface ResultFlags { }
private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
+ private ConnectionRecord mCurConnection;
private final Handler mHandler = new Handler();
private ServiceBinder mBinder;
MediaSession.Token mSession;
@@ -291,7 +292,8 @@
}
@Override
- public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
+ public void getMediaItem(final String mediaId, final ResultReceiver receiver,
+ final IMediaBrowserServiceCallbacks callbacks) {
if (TextUtils.isEmpty(mediaId) || receiver == null) {
return;
}
@@ -299,7 +301,13 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- performLoadItem(mediaId, receiver);
+ final IBinder b = callbacks.asBinder();
+ ConnectionRecord connection = mConnections.get(b);
+ if (connection == null) {
+ Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
+ return;
+ }
+ performLoadItem(mediaId, connection, receiver);
}
});
}
@@ -470,6 +478,20 @@
}
/**
+ * Gets the root hints sent from the currently connected {@link MediaBrowser}.
+ *
+ * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren}
+ * or {@link #onLoadItem}
+ */
+ public final Bundle getBrowserRootHints() {
+ if (mCurConnection == null) {
+ throw new IllegalStateException("This should be called inside of onLoadChildren or"
+ + " onLoadItem methods");
+ }
+ return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
+ }
+
+ /**
* Notifies all connected media browsers that the children of
* the specified parent id have changed in some way.
* This will cause browsers to fetch subscribed content again.
@@ -619,11 +641,13 @@
}
};
+ mCurConnection = connection;
if (options == null) {
onLoadChildren(parentId, result);
} else {
onLoadChildren(parentId, result, options);
}
+ mCurConnection = null;
if (!result.isDone()) {
throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
@@ -652,7 +676,8 @@
return list.subList(fromIndex, toIndex);
}
- private void performLoadItem(String itemId, final ResultReceiver receiver) {
+ private void performLoadItem(String itemId, final ConnectionRecord connection,
+ final ResultReceiver receiver) {
final Result<MediaBrowser.MediaItem> result =
new Result<MediaBrowser.MediaItem>(itemId) {
@Override
@@ -663,7 +688,9 @@
}
};
- MediaBrowserService.this.onLoadItem(itemId, result);
+ mCurConnection = connection;
+ onLoadItem(itemId, result);
+ mCurConnection = null;
if (!result.isDone()) {
throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 985fe3c..699f827 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -299,6 +299,11 @@
<!-- [CHAR LIMIT=50] Title of the settings section that displays general preferences
that are applicable to all engines, such as the speech rate -->
<string name="tts_general_section_title">General</string>
+ <!-- On main TTS Settings screen, in default settings section,
+ reset speech pitch of synthesized voice to 1x speech pitch. [CHAR LIMIT=50] -->
+ <string name="tts_reset_speech_pitch_title">Reset speech pitch</string>
+ <!--On main TTS Settings screen, summary for reset speech pitch of synthesized voice [CHAR LIMIT=150] -->
+ <string name="tts_reset_speech_pitch_summary">Reset the pitch at which the text is spoken to default.</string>
<!-- Default speech rate choices -->
<string-array name="tts_rate_entries">
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 502eed1..346ae20 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -336,7 +336,7 @@
launchBugreportInfoDialog(id);
break;
case INTENT_BUGREPORT_SCREENSHOT:
- takeScreenshot(id, true);
+ takeScreenshot(id);
break;
case INTENT_BUGREPORT_SHARE:
shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
@@ -417,8 +417,6 @@
return true;
}
mProcesses.put(info.id, info);
- // Take initial screenshot.
- takeScreenshot(id, false);
updateProgress(info);
return true;
}
@@ -635,19 +633,11 @@
/**
* Starting point for taking a screenshot.
* <p>
- * If {@code delayed} is set, it first display a toast message and waits
- * {@link #SCREENSHOT_DELAY_SECONDS} seconds before taking it, otherwise it takes the screenshot
- * right away.
- * <p>
- * Typical usage is delaying when taken from the notification action, and taking it right away
- * upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
+ * It first display a toast message and waits {@link #SCREENSHOT_DELAY_SECONDS} seconds before
+ * taking the screenshot.
*/
- private void takeScreenshot(int id, boolean delayed) {
- if (delayed) {
- // Only logs screenshots requested from the notification action.
- MetricsLogger.action(this,
- MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
- }
+ private void takeScreenshot(int id) {
+ MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
if (getInfo(id) == null) {
// Most likely am killed Shell before user tapped the notification. Since system might
// be too busy anwyays, it's better to ignore the notification and switch back to the
@@ -659,19 +649,15 @@
return;
}
setTakingScreenshot(true);
- if (delayed) {
- collapseNotificationBar();
- final String msg = mContext.getResources()
- .getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
- SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
- Log.i(TAG, msg);
- // Show a toast just once, otherwise it might be captured in the screenshot.
- Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
+ collapseNotificationBar();
+ final String msg = mContext.getResources()
+ .getQuantityString(com.android.internal.R.plurals.bugreport_countdown,
+ SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS);
+ Log.i(TAG, msg);
+ // Show a toast just once, otherwise it might be captured in the screenshot.
+ Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
- takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
- } else {
- takeScreenshot(id, 0);
- }
+ takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
}
/**
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 537e4c5..f76fb26 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -216,7 +216,7 @@
Bundle extras =
sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
- NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
+ NAME, NO_TITLE, NO_DESCRIPTION, 0, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
@@ -266,7 +266,7 @@
Bundle extras = acceptBugreportAndGetSharedIntent(ID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
- NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
+ NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
@@ -283,6 +283,8 @@
resetProperties();
sendBugreportStarted(1000);
+ waitForScreenshotButtonEnabled(true);
+ takeScreenshot();
sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
waitShareNotification(ID);
@@ -340,7 +342,7 @@
Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
mScreenshotPath);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
- NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+ NEW_NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
@@ -377,7 +379,7 @@
Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
plainText? mPlainTextPath : mZipPath, mScreenshotPath);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
- NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+ NEW_NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
@@ -404,7 +406,7 @@
Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
- NO_NAME, NO_TITLE, mDescription, 1, DIDNT_RENAME_SCREENSHOTS);
+ NO_NAME, NO_TITLE, mDescription, 0, DIDNT_RENAME_SCREENSHOTS);
assertServiceNotRunning();
}
@@ -449,7 +451,7 @@
// title.txt and description.txt entries.
extras = sendBugreportFinishedAndGetSharedIntent(ID2, mZipPath2, NO_SCREENSHOT);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID2, PID2, TITLE2,
- NEW_NAME2, TITLE2, DESCRIPTION2, 1, RENAMED_SCREENSHOTS);
+ NEW_NAME2, TITLE2, DESCRIPTION2, 0, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
@@ -500,7 +502,7 @@
// Finally, share bugreport.
Bundle extras = acceptBugreportAndGetSharedIntent(ID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
- NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
+ NAME, TITLE, mDescription, 0, RENAMED_SCREENSHOTS);
assertServiceNotRunning();
}
diff --git a/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml b/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
new file mode 100644
index 0000000..dc8e629
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_tv_card_thumbnail_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <solid
+ android:color="@color/recents_tv_card_background_color"/>
+ <corners
+ android:radius="@dimen/recents_tv_card_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
index d2ec52d..201f47d 100644
--- a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
@@ -30,7 +30,7 @@
android:layout_height="@dimen/recents_tv_screenshot_height"
android:gravity="center"
android:orientation="vertical"
- android:background="@color/recents_tv_card_background_color"
+ android:background="@drawable/recents_tv_card_thumbnail_background"
android:layout_centerHorizontal="true" >
<ImageView
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index c2de150..5fa802b 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -29,6 +29,7 @@
<dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
<dimen name="recents_tv_text_padding_start">12dip</dimen>
<dimen name="recents_tv_text_padding_bottom">12dip</dimen>
+ <dimen name="recents_tv_card_corner_radius">2dip</dimen>
<!-- Padding for grid view in recents view on tv -->
<dimen name="recents_tv_gird_row_top_margin">215dip</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index a72a7e6..235b782 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -21,6 +21,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Outline;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewOutlineProvider;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -49,6 +51,7 @@
private ImageView mBadgeView;
private Task mTask;
private boolean mDismissState;
+ private int mCornerRadius;
private ViewFocusAnimator mViewFocusAnimator;
private DismissAnimationsHolder mDismissAnimationsHolder;
@@ -77,6 +80,8 @@
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
mDismissAnimationsHolder = new DismissAnimationsHolder(this);
View title = findViewById(R.id.card_info_field);
+ mCornerRadius = getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
mViewFocusAnimator = new ViewFocusAnimator(this);
}
@@ -272,13 +277,18 @@
private void setAsScreenShotView(Bitmap screenshot, ImageView screenshotView) {
LayoutParams lp = (LayoutParams) screenshotView.getLayoutParams();
- lp.width = getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_card_width);
- lp.height = getResources()
- .getDimensionPixelSize(R.dimen.recents_tv_screenshot_height);
+ lp.width = LayoutParams.MATCH_PARENT;
+ lp.height = LayoutParams.MATCH_PARENT;
screenshotView.setLayoutParams(lp);
screenshotView.setImageBitmap(screenshot);
+ screenshotView.setClipToOutline(true);
+ screenshotView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+ }
+ });
}
private void setAsBannerView(Drawable banner, ImageView bannerView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e25f9de..6e9de23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -41,6 +41,7 @@
import android.widget.ImageView;
import android.widget.RemoteViews;
+import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.notification.HybridNotificationView;
@@ -103,6 +104,7 @@
private int mMaxExpandHeight;
private int mHeadsUpHeight;
private View mVetoButton;
+ private int mNotificationColor;
private boolean mClearable;
private ExpansionLogger mLogger;
private String mLoggingKey;
@@ -226,6 +228,7 @@
mPrivateLayout.onNotificationUpdated(entry);
mPublicLayout.onNotificationUpdated(entry);
mShowingPublicInitialized = false;
+ updateNotificationColor();
updateClearability();
if (mIsSummaryWithChildren) {
recreateNotificationHeader();
@@ -603,11 +606,12 @@
}
public int getNotificationColor() {
- int color = getStatusBarNotification().getNotification().color;
- if (color == Notification.COLOR_DEFAULT) {
- return mContext.getColor(com.android.internal.R.color.notification_icon_default_color);
- }
- return color;
+ return mNotificationColor;
+ }
+
+ private void updateNotificationColor() {
+ mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext,
+ getStatusBarNotification().getNotification().color);
}
public HybridNotificationView getSingleLineView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index ee483e5..be98d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -663,6 +663,11 @@
addView(divider, index);
mDividers.set(i, divider);
}
+ removeView(mOverflowNumber);
+ mOverflowNumber = null;
+ mOverflowInvertHelper = null;
+ mGroupOverFlowState = null;
+ updateGroupOverflow();
}
public void setUserLocked(boolean userLocked) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 229a3f4..ec05dae 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2875,6 +2875,19 @@
}
@Override
+ public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
+ userId, flags);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
try {
final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 820551d..9602bf6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -714,20 +714,24 @@
}
handleRemoveListLocked();
}
+
+ // Called only by Telecomm to communicate call state across different phone accounts. So
+ // there is no need to add a valid subId or slotId.
broadcastCallStateChanged(state, incomingNumber,
+ SubscriptionManager.INVALID_PHONE_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
- public void notifyCallStateForSubscriber(int subId, int state, String incomingNumber) {
+ public void notifyCallStateForPhoneId(int phoneId, int subId, int state,
+ String incomingNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
if (VDBG) {
- log("notifyCallStateForSubscriber: subId=" + subId
+ log("notifyCallStateForPhoneId: subId=" + subId
+ " state=" + state + " incomingNumber=" + incomingNumber);
}
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
mCallState[phoneId] = state;
mCallIncomingNumber[phoneId] = incomingNumber;
@@ -746,7 +750,7 @@
}
handleRemoveListLocked();
}
- broadcastCallStateChanged(state, incomingNumber, subId);
+ broadcastCallStateChanged(state, incomingNumber, phoneId, subId);
}
public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) {
@@ -788,31 +792,27 @@
}
handleRemoveListLocked();
}
- broadcastServiceStateChanged(state, subId);
+ broadcastServiceStateChanged(state, phoneId, subId);
}
- public void notifySignalStrength(SignalStrength signalStrength) {
- notifySignalStrengthForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- signalStrength);
- }
-
- public void notifySignalStrengthForSubscriber(int subId, SignalStrength signalStrength) {
+ public void notifySignalStrengthForPhoneId(int phoneId, int subId,
+ SignalStrength signalStrength) {
if (!checkNotifyPermission("notifySignalStrength()")) {
return;
}
if (VDBG) {
- log("notifySignalStrengthForSubscriber: subId=" + subId
- + " signalStrength=" + signalStrength);
- toStringLogSSC("notifySignalStrengthForSubscriber");
+ log("notifySignalStrengthForPhoneId: subId=" + subId
+ +" phoneId=" + phoneId + " signalStrength=" + signalStrength);
+ toStringLogSSC("notifySignalStrengthForPhoneId");
}
+
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
- if (VDBG) log("notifySignalStrengthForSubscriber: valid phoneId=" + phoneId);
+ if (VDBG) log("notifySignalStrengthForPhoneId: valid phoneId=" + phoneId);
mSignalStrength[phoneId] = signalStrength;
for (Record r : mRecords) {
if (VDBG) {
- log("notifySignalStrengthForSubscriber: r=" + r + " subId=" + subId
+ log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
if (r.matchPhoneStateListenerEvent(
@@ -820,7 +820,7 @@
idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
- log("notifySignalStrengthForSubscriber: callback.onSsS r=" + r
+ log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " ss=" + signalStrength);
}
@@ -835,7 +835,7 @@
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
if (DBG) {
- log("notifySignalStrengthForSubscriber: callback.onSS r=" + r
+ log("notifySignalStrengthForPhoneId: callback.onSS r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " gsmSS=" + gsmSignalStrength + " ss=" + ss);
}
@@ -846,11 +846,11 @@
}
}
} else {
- log("notifySignalStrengthForSubscriber: invalid phoneId=" + phoneId);
+ log("notifySignalStrengthForPhoneId: invalid phoneId=" + phoneId);
}
handleRemoveListLocked();
}
- broadcastSignalStrengthChanged(signalStrength, subId);
+ broadcastSignalStrengthChanged(signalStrength, phoneId, subId);
}
@Override
@@ -1347,7 +1347,7 @@
// the legacy intent broadcasting
//
- private void broadcastServiceStateChanged(ServiceState state, int subId) {
+ private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
long ident = Binder.clearCallingIdentity();
try {
mBatteryStats.notePhoneState(state.getState());
@@ -1363,10 +1363,12 @@
intent.putExtras(data);
// Pass the subscription along with the intent.
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int subId) {
+ private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
+ int subId) {
long ident = Binder.clearCallingIdentity();
try {
mBatteryStats.notePhoneSignalStrength(signalStrength);
@@ -1382,6 +1384,7 @@
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -1391,7 +1394,8 @@
* can be {@code SubscriptionManager.INVALID_SUBSCRIPTION_ID}, in which case we send
* a global state change broadcast ({@code TelephonyManager.ACTION_PHONE_STATE_CHANGED}).
*/
- private void broadcastCallStateChanged(int state, String incomingNumber, int subId) {
+ private void broadcastCallStateChanged(int state, String incomingNumber, int phoneId,
+ int subId) {
long ident = Binder.clearCallingIdentity();
try {
if (state == TelephonyManager.CALL_STATE_IDLE) {
@@ -1418,6 +1422,10 @@
intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
}
+ // If the phoneId is invalid, the broadcast is for overall call state.
+ if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+ }
// Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
// that have the runtime one
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 40430b4..8653f1a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -97,6 +97,7 @@
import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -341,6 +342,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -9742,7 +9744,7 @@
} else {
mStackSupervisor.resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
- allowResizeInDockedMode);
+ allowResizeInDockedMode, !DEFER_RESUME);
}
}
} finally {
@@ -11471,20 +11473,13 @@
}
synchronized (this) {
- if (mStackSupervisor.isFocusedUserLockedProfile()) {
+ if (mStackSupervisor.isUserLockedProfile(userId)) {
final long ident = Binder.clearCallingIdentity();
try {
final int currentUserId = mUserController.getCurrentUserIdLocked();
- // Get the focused task before launching launcher.
-
if (mUserController.isLockScreenDisabled(currentUserId)) {
-
// If there is no device lock, we will show the profile's credential page.
- // startActivityFromRecentsInner is intercepted and will forward user to it.
- if (mFocusedActivity != null) {
- mStackSupervisor.startActivityFromRecentsInner(
- mFocusedActivity.task.taskId, null);
- }
+ mActivityStarter.showConfirmDeviceCredential(userId);
} else {
// Showing launcher to avoid user entering credential twice.
startHomeActivityLocked(currentUserId, "notifyLockedProfile");
@@ -17966,46 +17961,11 @@
}
synchronized (this) {
final long origId = Binder.clearCallingIdentity();
- final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
- if (stack != null) {
- mWindowManager.deferSurfaceLayout();
- try {
- if (fromStackId == DOCKED_STACK_ID) {
-
- // We are moving all tasks from the docked stack to the fullscreen stack,
- // which is dismissing the docked stack, so resize all other stacks to
- // fullscreen here already so we don't end up with resize trashing.
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (StackId.isResizeableByDockedStack(i)) {
- ActivityStack otherStack = mStackSupervisor.getStack(i);
- if (otherStack != null) {
- mStackSupervisor.resizeStackLocked(i,
- null, null, null, PRESERVE_WINDOWS,
- true /* allowResizeInDockedMode */);
- }
- }
- }
- }
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- final int size = tasks.size();
- if (onTop) {
- for (int i = 0; i < size; i++) {
- mStackSupervisor.moveTaskToStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, onTop, !FORCE_FOCUS,
- "moveTasksToFullscreenStack", ANIMATE);
- }
- } else {
- for (int i = size - 1; i >= 0; i--) {
- mStackSupervisor.positionTaskInStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, 0);
- }
- }
- } finally {
- mWindowManager.continueSurfaceLayout();
- }
-
+ try {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
}
@@ -18227,7 +18187,7 @@
for (int stackId : resizedStacks) {
final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
mStackSupervisor.resizeStackLocked(
- stackId, newBounds, null, null, false, false);
+ stackId, newBounds, null, null, false, false, !DEFER_RESUME);
}
}
}
@@ -21006,6 +20966,13 @@
return mStackSupervisor.getTopVisibleActivities();
}
}
+
+ @Override
+ public void notifyDockedStackMinimizedChanged(boolean minimized) {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.setDockedStackMinimized(minimized);
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6e2cebb..8f7f396 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.Manifest;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
@@ -29,6 +30,8 @@
import android.app.IActivityContainerCallback;
import android.app.IActivityManager;
import android.app.IActivityManager.WaitResult;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.StatusBarManager;
@@ -37,6 +40,7 @@
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -166,6 +170,7 @@
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -238,6 +243,9 @@
// Restore task from the saved recents if it can't be found in any live stack.
static final boolean RESTORE_FROM_RECENTS = true;
+ // Don't execute any calls to resume.
+ static final boolean DEFER_RESUME = true;
+
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -342,9 +350,6 @@
/** Used on user changes */
final ArrayList<UserState> mStartingUsers = new ArrayList<>();
- /** Used to queue up any background users being started */
- final ArrayList<UserState> mStartingBackgroundUsers = new ArrayList<>();
-
/** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
* is being brought in front of us. */
boolean mUserLeaving = false;
@@ -432,6 +437,20 @@
*/
private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
+
+ /**
+ * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
+ * will be ignored. Useful for the case where the caller is handling resizing of other stack and
+ * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
+ * like the docked stack going empty.
+ */
+ private boolean mAllowDockedStackResize = true;
+
+ /**
+ * Is dock currently minimized.
+ */
+ boolean mIsDockMinimized;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -716,10 +735,44 @@
return null;
}
- boolean isFocusedUserLockedProfile() {
- final int userId = mFocusedStack.topRunningActivityLocked().userId;
- return userId != UserHandle.myUserId()
- && mService.mUserController.shouldConfirmCredentials(userId);
+ /**
+ * TODO: Handle freefom mode.
+ * @return true when credential confirmation is needed for the user and there is any
+ * activity started by the user in any visible stack.
+ */
+ boolean isUserLockedProfile(@UserIdInt int userId) {
+ if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+ return false;
+ }
+ final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+ final ActivityStack dockedStack = getStack(DOCKED_STACK_ID);
+ final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack};
+ for (final ActivityStack activityStack : activityStacks) {
+ if (activityStack == null) {
+ continue;
+ }
+ if (activityStack.topRunningActivityLocked() == null) {
+ continue;
+ }
+ if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) {
+ continue;
+ }
+ if (activityStack.isDockedStack() && mIsDockMinimized) {
+ continue;
+ }
+ final TaskRecord topTask = activityStack.topTask();
+ if (topTask == null) {
+ continue;
+ }
+ // To handle the case that work app is in the task but just is not the top one.
+ for (int i = topTask.mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord activityRecord = topTask.mActivities.get(i);
+ if (activityRecord.userId == userId) {
+ return true;
+ }
+ }
+ }
+ return false;
}
void setNextTaskIdForUserLocked(int taskId, int userId) {
@@ -1794,7 +1847,7 @@
if (StackId.resizeStackWithLaunchBounds(stackId)) {
resizeStackLocked(stackId, bounds,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */);
+ !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1921,7 +1974,7 @@
}
void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
- boolean preserveWindows, boolean allowResizeInDockedMode) {
+ boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
if (stackId == DOCKED_STACK_ID) {
resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
preserveWindows);
@@ -1943,7 +1996,10 @@
mWindowManager.deferSurfaceLayout();
try {
resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
- ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows);
+ if (!deferResume) {
+ ensureConfigurationAndResume(
+ stack, stack.topRunningActivityLocked(), preserveWindows);
+ }
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -2030,9 +2086,64 @@
}
}
+ void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
+ final ActivityStack stack = getStack(fromStackId);
+ if (stack == null) {
+ return;
+ }
+
+ mWindowManager.deferSurfaceLayout();
+ try {
+ if (fromStackId == DOCKED_STACK_ID) {
+
+ // We are moving all tasks from the docked stack to the fullscreen stack,
+ // which is dismissing the docked stack, so resize all other stacks to
+ // fullscreen here already so we don't end up with resize trashing.
+ for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+ if (StackId.isResizeableByDockedStack(i)) {
+ ActivityStack otherStack = getStack(i);
+ if (otherStack != null) {
+ resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS,
+ true /* allowResizeInDockedMode */, DEFER_RESUME);
+ }
+ }
+ }
+
+ // Also disable docked stack resizing since we have manually adjusted the
+ // size of other stacks above and we don't want to trigger a docked stack
+ // resize when we remove task from it below and it is detached from the
+ // display because it no longer contains any tasks.
+ mAllowDockedStackResize = false;
+ }
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ final int size = tasks.size();
+ if (onTop) {
+ for (int i = 0; i < size; i++) {
+ moveTaskToStackLocked(tasks.get(i).taskId,
+ FULLSCREEN_WORKSPACE_STACK_ID, onTop, !FORCE_FOCUS,
+ "moveTasksToFullscreenStack", ANIMATE);
+ }
+ } else {
+ for (int i = size - 1; i >= 0; i--) {
+ positionTaskInStackLocked(tasks.get(i).taskId,
+ FULLSCREEN_WORKSPACE_STACK_ID, 0);
+ }
+ }
+ } finally {
+ mAllowDockedStackResize = true;
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
+
+ if (!mAllowDockedStackResize) {
+ // Docked stack resize currently disabled.
+ return;
+ }
+
final ActivityStack stack = getStack(DOCKED_STACK_ID);
if (stack == null) {
Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
@@ -2042,6 +2153,8 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
mWindowManager.deferSurfaceLayout();
try {
+ // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
+ mAllowDockedStackResize = false;
ActivityRecord r = stack.topRunningActivityLocked();
resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
tempDockedTaskInsetBounds);
@@ -2052,20 +2165,7 @@
// The dock stack either was dismissed or went fullscreen, which is kinda the same.
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
- resizeStackLocked(i, null, null, null, preserveWindows,
- true /* allowResizeInDockedMode */);
- }
- }
-
- ArrayList<TaskRecord> tasks = stack.getAllTasks();
- final int count = tasks.size();
- for (int i = 0; i < count; i++) {
- moveTaskToStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
- false /* animate */);
- }
+ moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP);
// stack shouldn't contain anymore activities, so nothing to resume.
r = null;
@@ -2080,12 +2180,13 @@
if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
resizeStackLocked(i, tempRect, tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
- true /* allowResizeInDockedMode */);
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
}
}
}
ensureConfigurationAndResume(stack, r, preserveWindows);
} finally {
+ mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -2444,7 +2545,7 @@
// animation bounds for the pinned stack to the desired bounds the caller wants.
resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
- true /* allowResizeInDockedMode */);
+ true /* allowResizeInDockedMode */, !DEFER_RESUME);
if (task.mActivities.size() == 1) {
// There is only one activity in the task. So, we can just move the task over to
@@ -2487,8 +2588,6 @@
stack.positionTask(task, position);
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
- stack.ensureActivityConfigurationLocked(task.topRunningActivityLocked(), 0,
- !PRESERVE_WINDOWS);
stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
}
@@ -3590,6 +3689,22 @@
}
}
+ void setDockedStackMinimized(boolean minimized) {
+ mIsDockMinimized = minimized;
+ if (minimized) {
+ // Docked stack is not visible, no need to confirm credentials for its top activity.
+ return;
+ }
+ final ActivityStack dockedStack = getStack(StackId.DOCKED_STACK_ID);
+ if (dockedStack == null) {
+ return;
+ }
+ final ActivityRecord top = dockedStack.topRunningActivityLocked();
+ if (top != null && mService.mUserController.shouldConfirmCredentials(top.userId)) {
+ mService.mActivityStarter.showConfirmDeviceCredential(top.userId);
+ }
+ }
+
private final class ActivityStackSupervisorHandler extends Handler {
public ActivityStackSupervisorHandler(Looper looper) {
@@ -4241,28 +4356,31 @@
? new ActivityOptions(bOptions) : null;
final int launchStackId = (activityOptions != null)
? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
-
if (launchStackId == HOME_STACK_ID) {
throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ taskId + " can't be launch in the home stack.");
}
+
+ if (launchStackId == DOCKED_STACK_ID) {
+ mWindowManager.setDockedStackCreateState(
+ activityOptions.getDockCreateMode(), null /* initialBounds */);
+
+ // Defer updating the stack in which recents is until the app transition is done, to
+ // not run into issues where we still need to draw the task in recents but the
+ // docked stack is already created.
+ deferUpdateBounds(HOME_STACK_ID);
+ mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+ }
+
task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
if (task == null) {
+ continueUpdateBounds(HOME_STACK_ID);
+ mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecentsInner: Task " + taskId + " not found.");
}
if (launchStackId != INVALID_STACK_ID) {
- if (launchStackId == DOCKED_STACK_ID) {
- mWindowManager.setDockedStackCreateState(
- activityOptions.getDockCreateMode(), null /* initialBounds */);
-
- // Defer updating the stack in which recents is until the app transition is done, to
- // not run into issues where we still need to draw the task in recents but the
- // docked stack is already created.
- deferUpdateBounds(HOME_STACK_ID);
- mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
- }
if (task.stack.mStackId != launchStackId) {
moveTaskToStackLocked(
taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
@@ -4290,12 +4408,12 @@
intent = task.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.userId;
- int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
- null, null, 0, 0, bOptions, userId, null, task);
- if (launchStackId == DOCKED_STACK_ID) {
- setResizingDuringAnimation(task.taskId);
- }
- return result;
+ int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
+ null, null, 0, 0, bOptions, userId, null, task);
+ if (launchStackId == DOCKED_STACK_ID) {
+ setResizingDuringAnimation(task.taskId);
+ }
+ return result;
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 785dd47..566d8d9 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -29,6 +29,7 @@
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.IIntentSender;
@@ -76,6 +77,7 @@
ActivityInfo mAInfo;
String mResolvedType;
TaskRecord mInTask;
+ ActivityOptions mActivityOptions;
ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
mService = service;
@@ -92,7 +94,7 @@
}
void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
- TaskRecord inTask, int callingPid, int callingUid) {
+ TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
mUserManager = UserManager.get(mService.mContext);
mIntent = intent;
mCallingPid = callingPid;
@@ -101,6 +103,7 @@
mAInfo = aInfo;
mResolvedType = resolvedType;
mInTask = inTask;
+ mActivityOptions = activityOptions;
if (interceptSuspendPackageIfNeed()) {
// Skip the rest of interceptions as the package is suspended by device admin so
// no user action can undo this.
@@ -177,6 +180,12 @@
mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
mInTask = null;
}
+ if (mActivityOptions == null) {
+ mActivityOptions = ActivityOptions.makeBasic();
+ }
+ // Showing credential confirmation activity in home task to avoid stopping multi-windowed
+ // mode after showing the full-screen credential confirmation activity.
+ mActivityOptions.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
final UserInfo parent = mUserManager.getProfileParent(mUserId);
mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4075230a..9c93f2c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -82,9 +82,11 @@
import android.app.IActivityContainer;
import android.app.IActivityManager;
import android.app.IApplicationThread;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
@@ -380,7 +382,8 @@
}
mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
- mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid);
+ mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid,
+ options);
intent = mInterceptor.mIntent;
rInfo = mInterceptor.mRInfo;
aInfo = mInterceptor.mAInfo;
@@ -388,7 +391,7 @@
inTask = mInterceptor.mInTask;
callingPid = mInterceptor.mCallingPid;
callingUid = mInterceptor.mCallingUid;
-
+ options = mInterceptor.mActivityOptions;
if (abort) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
@@ -589,6 +592,53 @@
}
}
+ void showConfirmDeviceCredential(int userId) {
+ // First, retrieve the stack that we want to resume after credential is confirmed.
+ ActivityStack targetStack;
+ ActivityStack fullscreenStack =
+ mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+ if (fullscreenStack != null &&
+ fullscreenStack.getStackVisibilityLocked(null) != ActivityStack.STACK_INVISIBLE) {
+ // Single window case and the case that the docked stack is shown with fullscreen stack.
+ targetStack = fullscreenStack;
+ } else {
+ // The case that the docked stack is shown with recent.
+ targetStack = mSupervisor.getStack(HOME_STACK_ID);
+ }
+ if (targetStack == null) {
+ return;
+ }
+ final KeyguardManager km = (KeyguardManager) mService.mContext
+ .getSystemService(Context.KEYGUARD_SERVICE);
+ final Intent credential =
+ km.createConfirmDeviceCredentialIntent(null, null, userId);
+ credential.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+ Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
+ final ActivityRecord activityRecord = targetStack.topRunningActivityLocked();
+ if (activityRecord != null) {
+ final IIntentSender target = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY,
+ activityRecord.launchedFromPackage,
+ activityRecord.launchedFromUid,
+ activityRecord.userId,
+ null, null, 0,
+ new Intent[] { activityRecord.intent },
+ new String[] { activityRecord.resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT |
+ PendingIntent.FLAG_ONE_SHOT |
+ PendingIntent.FLAG_IMMUTABLE,
+ null);
+ credential.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+ // Show confirm credentials activity.
+ mService.mContext.startActivityAsUser(credential, options.toBundle(),
+ UserHandle.CURRENT);
+ }
+ }
+
+
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 2dafa3e..c770620 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -222,7 +222,7 @@
removeClient(client);
}
if (DEBUG) Slog.v(TAG, "handleError(client="
- + client != null ? client.getOwnerString() : "null" + ", error = " + error + ")");
+ + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
// This is the magic code that starts the next client when the old client finishes.
if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
mHandler.removeCallbacks(mResetClientState);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a11ee74..66c1a53 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -174,12 +174,13 @@
mInstaller.execute("destroy_app_profiles", pkgName);
}
- public void createUserConfig(int userid) throws InstallerException {
- mInstaller.execute("mkuserconfig", userid);
+ public void createUserData(String uuid, int userId, int userSerial, int flags)
+ throws InstallerException {
+ mInstaller.execute("create_user_data", uuid, userId, userSerial, flags);
}
- public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
- mInstaller.execute("rmuser", uuid, userid);
+ public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
+ mInstaller.execute("destroy_user_data", uuid, userId, flags);
}
public void markBootComplete(String instructionSet) throws InstallerException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dc81c65..4819de5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -109,7 +109,6 @@
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
-import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -13420,14 +13419,17 @@
final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
move.dataAppName);
Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
+ final int[] userIds = sUserManager.getUserIds();
synchronized (mInstallLock) {
// Clean up both app data and code
// All package moves are frozen until finished
- try {
- mInstaller.destroyAppData(volumeUuid, move.packageName, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
- } catch (InstallerException e) {
- Slog.w(TAG, String.valueOf(e));
+ for (int userId : userIds) {
+ try {
+ mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+ } catch (InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
+ }
}
removeCodePathLI(codeFile);
}
@@ -17421,6 +17423,10 @@
return true;
}
});
+
+ // Now that we're mostly running, clean up stale users and apps
+ reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
+ reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
}
@Override
@@ -18653,14 +18659,100 @@
}
/**
+ * Prepare storage areas for given user on all mounted devices.
+ */
+ void prepareUserData(int userId, int userSerial, int flags) {
+ synchronized (mInstallLock) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+ final String volumeUuid = vol.getFsUuid();
+ prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+ }
+ }
+ }
+
+ private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+ boolean allowRecover) {
+ // Prepare storage and verify that serial numbers are consistent; if
+ // there's a mismatch we need to destroy to avoid leaking data
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ try {
+ storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+
+ if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+ UserManagerService.enforceSerialNumber(
+ Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
+ }
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+ UserManagerService.enforceSerialNumber(
+ Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
+ }
+
+ synchronized (mInstallLock) {
+ mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
+ }
+ } catch (Exception e) {
+ logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
+ + " because we failed to prepare: " + e);
+ destroyUserDataLI(volumeUuid, userId, flags);
+
+ if (allowRecover) {
+ // Try one last time; if we fail again we're really in trouble
+ prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
+ }
+ }
+ }
+
+ /**
+ * Destroy storage areas for given user on all mounted devices.
+ */
+ void destroyUserData(int userId, int flags) {
+ synchronized (mInstallLock) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+ final String volumeUuid = vol.getFsUuid();
+ destroyUserDataLI(volumeUuid, userId, flags);
+ }
+ }
+ }
+
+ private void destroyUserDataLI(String volumeUuid, int userId, int flags) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ try {
+ // Clean up app data, profile data, and media data
+ mInstaller.destroyUserData(volumeUuid, userId, flags);
+
+ // Clean up system data
+ if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+ if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+ FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
+ FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
+ }
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+ FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
+ }
+ }
+
+ // Data with special labels is now gone, so finish the job
+ storage.destroyUserStorage(volumeUuid, userId, flags);
+
+ } catch (Exception e) {
+ logCriticalInfo(Log.WARN,
+ "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
+ }
+ }
+
+ /**
* Examine all users present on given mounted volume, and destroy data
* belonging to users that are no longer valid, or whose user ID has been
* recycled.
*/
private void reconcileUsers(String volumeUuid) {
- // TODO: also reconcile DE directories
- final File[] files = FileUtils
- .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
+ final List<File> files = new ArrayList<>();
+ Collections.addAll(files, FileUtils
+ .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
+ Collections.addAll(files, FileUtils
+ .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
for (File file : files) {
if (!file.isDirectory()) continue;
@@ -18691,11 +18783,8 @@
if (destroyUser) {
synchronized (mInstallLock) {
- try {
- mInstaller.removeUserDataDirs(volumeUuid, userId);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to clean up user dirs", e);
- }
+ destroyUserDataLI(volumeUuid, userId,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
}
}
}
@@ -19490,21 +19579,7 @@
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
- }
- synchronized (mInstallLock) {
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
- try {
- mInstaller.removeUserDataDirs(volumeUuid, userHandle);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to remove user data", e);
- }
- }
- synchronized (mPackages) {
- removeUnusedPackagesLILPw(userManager, userHandle);
- }
+ removeUnusedPackagesLPw(userManager, userHandle);
}
}
@@ -19513,7 +19588,7 @@
* that are no longer in use by any other user.
* @param userHandle the user being removed
*/
- private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+ private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
final boolean DEBUG_CLEAN_APKS = false;
int [] users = userManager.getUserIds();
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
@@ -19563,11 +19638,6 @@
/** Called by UserManagerService */
void createNewUser(int userHandle) {
synchronized (mInstallLock) {
- try {
- mInstaller.createUserConfig(userHandle);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to create user config", e);
- }
mSettings.createNewUserLI(this, mInstaller, userHandle);
}
synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9b918f3..42f7166 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -65,7 +65,6 @@
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -2060,7 +2059,7 @@
}
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
- prepareUserStorage(userId, userInfo.serialNumber,
+ mPm.prepareUserData(userId, userInfo.serialNumber,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
mPm.createNewUser(userId);
userInfo.partial = false;
@@ -2300,9 +2299,9 @@
Slog.i(LOG_TAG,
"Destroying key for user " + userHandle + " failed, continuing anyway", e);
}
+
// Cleanup package manager settings
mPm.cleanUpUser(this, userHandle);
-
// Remove this user from the list
synchronized (mUsersLock) {
mUsers.remove(userHandle);
@@ -2322,24 +2321,12 @@
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
userFile.delete();
updateUserIds();
- File userDir = Environment.getUserSystemDirectory(userHandle);
- File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
- if (userDir.renameTo(renamedUserDir)) {
- removeDirectoryRecursive(renamedUserDir);
- } else {
- removeDirectoryRecursive(userDir);
- }
- }
- private void removeDirectoryRecursive(File parent) {
- if (parent.isDirectory()) {
- String[] files = parent.list();
- for (String filename : files) {
- File child = new File(parent, filename);
- removeDirectoryRecursive(child);
- }
- }
- parent.delete();
+ // Now that we've purged all the metadata above, destroy the actual data
+ // on disk; if we battery pull in here we'll finish cleaning up when
+ // reconciling after reboot.
+ mPm.destroyUserData(userHandle,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
}
private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
@@ -2634,23 +2621,12 @@
}
/**
- * Prepare storage areas for given user on all mounted devices.
- */
- private void prepareUserStorage(int userId, int userSerial, int flags) {
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
- }
- }
-
- /**
* Called right before a user is started. This gives us a chance to prepare
* app storage and apply any user restrictions.
*/
public void onBeforeStartUser(int userId) {
final int userSerial = getUserSerialNumber(userId);
- prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+ mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
if (userId != UserHandle.USER_SYSTEM) {
@@ -2666,7 +2642,7 @@
*/
public void onBeforeUnlockUser(@UserIdInt int userId) {
final int userSerial = getUserSerialNumber(userId);
- prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+ mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 49ff385..890a715 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,10 +19,15 @@
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
@@ -44,6 +49,7 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.utils.ManagedApplicationService.PendingEvent;
import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
@@ -56,6 +62,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -85,6 +92,8 @@
public static final String VR_MANAGER_BINDER_SERVICE = "vrmanager";
+ private static final int PENDING_STATE_DELAY_MS = 300;
+
private static native void initializeNative();
private static native void setVrModeNative(boolean enabled);
@@ -107,8 +116,10 @@
private String mPreviousNotificationPolicyAccessPackage;
private String mPreviousCoarseLocationPackage;
private String mPreviousManageOverlayPackage;
+ private VrState mPendingState;
private static final int MSG_VR_STATE_CHANGE = 0;
+ private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
private final Handler mHandler = new Handler() {
@Override
@@ -127,12 +138,32 @@
}
mRemoteCallbacks.finishBroadcast();
} break;
+ case MSG_PENDING_VR_STATE_CHANGE : {
+ synchronized(mLock) {
+ VrManagerService.this.consumeAndApplyPendingStateLocked();
+ }
+ } break;
default :
throw new IllegalStateException("Unknown message type: " + msg.what);
}
}
};
+ private static class VrState {
+ final boolean enabled;
+ final int userId;
+ final ComponentName targetPackageName;
+ final ComponentName callingPackage;
+
+ VrState(boolean enabled, ComponentName targetPackageName, int userId,
+ ComponentName callingPackage) {
+ this.enabled = enabled;
+ this.userId = userId;
+ this.targetPackageName = targetPackageName;
+ this.callingPackage = callingPackage;
+ }
+ };
+
private static final BinderChecker sBinderChecker = new BinderChecker() {
@Override
public IInterface asInterface(IBinder binder) {
@@ -156,6 +187,13 @@
return; // No active services
}
+ // If there is a pending state change, we'd better deal with that first
+ consumeAndApplyPendingStateLocked();
+
+ if (mCurrentVrService == null) {
+ return; // No active services
+ }
+
// There is an active service, update it if needed
updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
mCurrentVrService.getUserId(), null);
@@ -258,6 +296,93 @@
publishLocalService(VrManagerInternal.class, new LocalService());
publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
+
+ // If there are no VR packages installed on the device, then disable VR
+ // components, otherwise, enable them.
+ setEnabledStatusOfVrComponents();
+ }
+
+ private void setEnabledStatusOfVrComponents() {
+ ArraySet<ComponentName> vrComponents = SystemConfig.getInstance().getDefaultVrComponents();
+ if (vrComponents == null) {
+ return;
+ }
+
+ // We only want to enable VR components if there is a VR package installed on the device.
+ // The VR components themselves do not quality as a VR package, so exclude them.
+ ArraySet<String> vrComponentPackageNames = new ArraySet<>();
+ for (ComponentName componentName : vrComponents) {
+ vrComponentPackageNames.add(componentName.getPackageName());
+ }
+
+ // Check to see if there are any packages on the device, other than the VR component
+ // packages.
+ PackageManager pm = mContext.getPackageManager();
+ List<PackageInfo> packageInfos = pm.getInstalledPackages(
+ PackageManager.GET_CONFIGURATIONS);
+ boolean vrModeIsUsed = false;
+ for (PackageInfo packageInfo : packageInfos) {
+ if (packageInfo != null && packageInfo.packageName != null &&
+ pm.getApplicationEnabledSetting(packageInfo.packageName) ==
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ vrModeIsUsed = enableVrComponentsIfVrModeUsed(pm, packageInfo,
+ vrComponentPackageNames, vrComponents);
+ if (vrModeIsUsed) {
+ break;
+ }
+ }
+ }
+
+ if (!vrModeIsUsed) {
+ Slog.i(TAG, "No VR packages found, disabling VR components");
+ for (ComponentName componentName : vrComponents) {
+ pm.setApplicationEnabledSetting(componentName.getPackageName(),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ }
+
+ // Register to receive an intent when a new package is installed, in case that package
+ // requires VR components.
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PackageManager pm = context.getPackageManager();
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName != null) {
+ try {
+ PackageInfo packageInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_CONFIGURATIONS);
+ enableVrComponentsIfVrModeUsed(pm, packageInfo,
+ vrComponentPackageNames, vrComponents);
+ } catch (NameNotFoundException e) {
+ }
+ }
+ };
+ }, intentFilter);
+ }
+ }
+
+ private boolean enableVrComponentsIfVrModeUsed(PackageManager pm, PackageInfo packageInfo,
+ ArraySet<String> vrComponentPackageNames, ArraySet<ComponentName> vrComponents) {
+ boolean isVrComponent = vrComponents != null &&
+ vrComponentPackageNames.contains(packageInfo.packageName);
+ if (packageInfo != null && packageInfo.reqFeatures != null && !isVrComponent) {
+ for (FeatureInfo featureInfo : packageInfo.reqFeatures) {
+ if (featureInfo.name != null &&
+ (featureInfo.name.equals(PackageManager.FEATURE_VR_MODE) ||
+ featureInfo.name.equals(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE))) {
+ Slog.i(TAG, "VR package found, enabling VR components");
+ for (ComponentName componentName : vrComponents) {
+ pm.setApplicationEnabledSetting(componentName.getPackageName(),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
}
@Override
@@ -679,14 +804,40 @@
sBinderChecker);
}
+ private void consumeAndApplyPendingStateLocked() {
+ if (mPendingState != null) {
+ updateCurrentVrServiceLocked(mPendingState.enabled,
+ mPendingState.targetPackageName, mPendingState.userId,
+ mPendingState.callingPackage);
+ mPendingState = null;
+ }
+ }
+
/*
* Implementation of VrManagerInternal calls. These are callable from system services.
*/
- private boolean setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
+ private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
int userId, @NonNull ComponentName callingPackage) {
+
synchronized (mLock) {
- return updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
+
+ if (!enabled && mCurrentVrService != null) {
+ // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
+ // and service bind/unbind in case we are immediately switching to another VR app.
+ if (mPendingState == null) {
+ mHandler.sendEmptyMessageDelayed(MSG_PENDING_VR_STATE_CHANGE,
+ PENDING_STATE_DELAY_MS);
+ }
+
+ mPendingState = new VrState(enabled, targetPackageName, userId, callingPackage);
+ return;
+ } else {
+ mHandler.removeMessages(MSG_PENDING_VR_STATE_CHANGE);
+ mPendingState = null;
+ }
+
+ updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
}
}
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index c240d07..52146c2 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -149,8 +149,10 @@
if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
// Dim should cover the entire screen for system windows.
mDisplayContent.getLogicalDisplayRect(mTmpBounds);
- state.dimLayer.setBounds(mTmpBounds);
+ } else {
+ dimLayerUser.getDimBounds(mTmpBounds);
}
+ state.dimLayer.setBounds(mTmpBounds);
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 3bd7a96..0039c0a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -27,6 +27,7 @@
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
import android.content.Context;
import android.graphics.Rect;
@@ -84,9 +85,12 @@
private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
+ private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private int mDividerWindowWidth;
+ private int mDividerWindowWidthInactive;
private int mDividerInsets;
private boolean mResizing;
private WindowState mWindow;
@@ -130,6 +134,8 @@
com.android.internal.R.dimen.docked_stack_divider_thickness);
mDividerInsets = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
+ mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
+ DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
}
void onConfigurationChanged() {
@@ -148,6 +154,10 @@
return mDividerInsets;
}
+ int getContentWidthInactive() {
+ return mDividerWindowWidthInactive;
+ }
+
void setResizing(boolean resizing) {
if (mResizing != resizing) {
mResizing = resizing;
@@ -276,6 +286,9 @@
}
void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+ mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
+ mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
+ minimizedDock ? 1 : 0, 0).sendToTarget();
final int size = mDockedStackListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
@@ -355,6 +368,12 @@
return;
}
+ // If the app that having visibility change is not the top visible one in the task,
+ // it does not affect whether the docked stack is minimized, ignore it.
+ if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
+ return;
+ }
+
// If the stack is completely offscreen, this might just be an intermediate state when
// docking a task/launching recents at the same time, but home doesn't actually get
// visible after the state settles in.
@@ -644,4 +663,4 @@
public String toShortString() {
return TAG;
}
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c322cd8..872bc6d 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -53,6 +53,12 @@
public class TaskStack implements DimLayer.DimLayerUser,
BoundsAnimationController.AnimateBoundsUser {
+ /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+ * restrict IME adjustment so that a min portion of top stack remains visible.*/
+ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+ /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
+ private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
/** Unique identifier */
final int mStackId;
@@ -249,7 +255,12 @@
task.scrollLocked(mTmpRect2);
} else if (task.isResizeable() && task.mOverrideConfig != Configuration.EMPTY) {
task.getBounds(mTmpRect2);
- mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+ if (mAdjustedForIme && getDockSide() == DOCKED_TOP) {
+ int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
+ mTmpRect2.offset(0, offsetY);
+ } else {
+ mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+ }
task.setTempInsetBounds(tempInsetBounds);
task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
}
@@ -882,6 +893,7 @@
mImeGoingAway = false;
mAdjustImeAmount = 0f;
updateAdjustedBounds();
+ mService.setResizeDimLayer(false, mStackId, 1.0f);
} else {
mImeGoingAway |= mAdjustedForIme;
}
@@ -930,6 +942,11 @@
}
}
+ int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
+ return displayContentRect.top + (int)
+ ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+ }
+
private boolean adjustForIME(final WindowState imeWin) {
final int dockedSide = getDockSide();
final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
@@ -953,23 +970,41 @@
mLastContentBounds.set(contentBounds);
final int yOffset = displayContentRect.bottom - contentBounds.bottom;
+ final int dividerWidth =
+ getDisplayContent().mDividerControllerLocked.getContentWidth();
+ final int dividerWidthInactive =
+ getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
+
if (dockedSide == DOCKED_TOP) {
// If this stack is docked on top, we make it smaller so the bottom stack is not
- // occluded by IME. We shift its bottom up by the height of the IME (capped by
- // the display content rect). Note that we don't change the task bounds.
- int bottom = Math.max(
- mBounds.bottom - yOffset, displayContentRect.top);
+ // occluded by IME. We shift its bottom up by the height of the IME, but
+ // leaves at least 30% of the top stack visible.
+ final int minTopStackBottom =
+ getMinTopStackBottom(displayContentRect, mBounds.bottom);
+ final int bottom = Math.max(
+ mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive,
+ minTopStackBottom);
mTmpAdjustedBounds.set(mBounds);
mTmpAdjustedBounds.bottom =
(int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
mFullyAdjustedImeBounds.set(mBounds);
} else {
- // If this stack is docked on bottom, we shift it up so that it's not occluded by
- // IME. We try to move it up by the height of the IME window (although the best
- // we could do is to make the top stack fully collapsed).
- final int dividerWidth = getDisplayContent().mDividerControllerLocked
- .getContentWidth();
- int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
+ final int top;
+ final boolean isFocusedStack = mService.getFocusedStackLocked() == this;
+ if (isFocusedStack) {
+ // If this stack is docked on bottom and has focus, we shift it up so that it's not
+ // occluded by IME. We try to move it up by the height of the IME window, but only
+ // to the extent that leaves at least 30% of the top stack visible.
+ final int minTopStackBottom =
+ getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth);
+ top = Math.max(
+ mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive);
+ } else {
+ // If this stack is docked on bottom but doesn't have focus, we don't need to adjust
+ // for IME, but still need to apply a small adjustment due to the thinner divider.
+ top = mBounds.top - dividerWidth + dividerWidthInactive;
+ }
+
mTmpAdjustedBounds.set(mBounds);
mTmpAdjustedBounds.top =
(int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
@@ -1043,6 +1078,12 @@
mLastContentBounds.setEmpty();
}
setAdjustedBounds(mTmpAdjustedBounds);
+
+ final boolean isFocusedStack = mService.getFocusedStackLocked() == this;
+ if (mAdjustedForIme && adjust && !isFocusedStack) {
+ final float alpha = mAdjustImeAmount * IME_ADJUST_DIM_AMOUNT;
+ mService.setResizeDimLayer(true, mStackId, alpha);
+ }
}
boolean isAdjustedForMinimizedDockedStack() {
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 0217c09..ed1f428 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -203,6 +203,10 @@
if (mDockDivider != null && mDockDivider.isVisibleLw()
&& mService.mInputMethodWindow != null) {
layer = assignAndIncreaseLayerIfNeeded(mService.mInputMethodWindow, layer);
+ for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
+ final WindowState dialog = mService.mInputMethodDialogs.get(i);
+ layer = assignAndIncreaseLayerIfNeeded(dialog, layer);
+ }
}
// We know that we will be animating a relaunching window in the near future, which will
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2e0c187..875ad92 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7445,17 +7445,16 @@
void adjustForImeIfNeeded(final DisplayContent displayContent) {
final WindowState imeWin = mInputMethodWindow;
- final TaskStack focusedStack =
- mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+ final TaskStack focusedStack = getFocusedStackLocked();
final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
- && dockVisible
- && focusedStack != null
- && focusedStack.getDockSide() == DOCKED_BOTTOM){
+ && dockVisible && focusedStack != null) {
+ final boolean isFocusOnBottom = focusedStack.getDockSide() == DOCKED_BOTTOM;
final ArrayList<TaskStack> stacks = displayContent.getStacks();
for (int i = stacks.size() - 1; i >= 0; --i) {
final TaskStack stack = stacks.get(i);
- if (stack.isVisibleLocked()) {
+ final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
+ if (stack.isVisibleLocked() && (isFocusOnBottom || isDockedOnBottom)) {
stack.setAdjustedForIme(imeWin);
}
}
@@ -7599,6 +7598,10 @@
return mCurrentFocus;
}
+ TaskStack getFocusedStackLocked() {
+ return mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+ }
+
private void showAuditSafeModeNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
new Intent(Intent.ACTION_VIEW,
@@ -7800,6 +7803,8 @@
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_REMOVE_TIMEOUT = 52;
+ public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -8423,6 +8428,10 @@
}
}
break;
+ case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
+ mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -9333,6 +9342,7 @@
WindowState newFocus = computeFocusedWindowLocked();
if (mCurrentFocus != newFocus) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+ TaskStack oldFocusedStack = getFocusedStackLocked();
// This check makes sure that we don't already have the focus
// change message pending.
mH.removeMessages(H.REPORT_FOCUS_CHANGE);
@@ -9354,6 +9364,7 @@
mLosingFocus.remove(newFocus);
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
+ TaskStack newFocusedStack = getFocusedStackLocked();
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
@@ -9383,6 +9394,20 @@
mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
}
+ // TODO: Reset and re-apply IME adjustment if needed when stack focus changed.
+ // This makes sure divider starts an animation from pre-adjust position to final
+ // position. Ideally we want to skip the reset and animation from current position
+ // directly to final position.
+ final WindowState imeWin = mInputMethodWindow;
+ if (oldFocusedStack != null) {
+ oldFocusedStack.resetAdjustedForIme(true);
+ }
+ if (newFocusedStack != null) {
+ newFocusedStack.resetAdjustedForIme(true);
+ }
+ displayContent.mDividerControllerLocked.setAdjustedForIme(false, false, imeWin);
+ adjustForImeIfNeeded(displayContent);
+
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bf69717..5077f32 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2608,7 +2608,7 @@
final int ph = containingFrame.height();
final Task task = getTask();
final boolean nonFullscreenTask = isInMultiWindowMode();
- final boolean fitToDisplay = task != null && !nonFullscreenTask && !layoutInParentFrame();
+ final boolean fitToDisplay = (task == null || !nonFullscreenTask) && !layoutInParentFrame();
float x, y;
int w,h;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f5c58c5..da0d048 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -225,6 +225,30 @@
"android.telecom.extra.UNKNOWN_CALL_HANDLE";
/**
+ * Optional extra for incoming and outgoing calls containing a long which specifies the time the
+ * call was created. This value is in milliseconds since boot.
+ * @hide
+ */
+ public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
+ "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
+
+ /**
+ * Optional extra for incoming and outgoing calls containing a long which specifies the time
+ * telecom began routing the call. This value is in milliseconds since boot.
+ * @hide
+ */
+ public static final String EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS =
+ "android.telecom.extra.CALL_TELECOM_ROUTING_START_TIME_MILLIS";
+
+ /**
+ * Optional extra for incoming and outgoing calls containing a long which specifies the time
+ * telecom finished routing the call. This value is in milliseconds since boot.
+ * @hide
+ */
+ public static final String EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS =
+ "android.telecom.extra.CALL_TELECOM_ROUTING_END_TIME_MILLIS";
+
+ /**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the disconnect code.
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 26730de..5ac697f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -547,6 +547,13 @@
*/
public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+ /**
+ * Flag to indicate if Wi-Fi needs to be disabled in ECBM
+ * @hide
+ **/
+ public static final String
+ KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+
/**
* List operator-specific error codes and indices of corresponding error strings in
* wfcOperatorErrorAlertMessages and wfcOperatorErrorNotificationMessages.
@@ -755,6 +762,7 @@
sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+ sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index ff8c71c..f3b0ce2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1347,6 +1347,7 @@
if (subInfo != null) {
newConfig.mcc = subInfo.getMcc();
newConfig.mnc = subInfo.getMnc();
+ if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
}
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
DisplayMetrics newMetrics = new DisplayMetrics();
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 907d76e..2c6be62 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -37,10 +37,10 @@
void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events,
boolean notifyNow);
void notifyCallState(int state, String incomingNumber);
- void notifyCallStateForSubscriber(in int subId, int state, String incomingNumber);
+ void notifyCallStateForPhoneId(in int phoneId, in int subId, int state, String incomingNumber);
void notifyServiceStateForPhoneId(in int phoneId, in int subId, in ServiceState state);
- void notifySignalStrength(in SignalStrength signalStrength);
- void notifySignalStrengthForSubscriber(in int subId, in SignalStrength signalStrength);
+ void notifySignalStrengthForPhoneId(in int phoneId, in int subId,
+ in SignalStrength signalStrength);
void notifyMessageWaitingChangedForPhoneId(in int phoneId, in int subId, in boolean mwi);
void notifyCallForwardingChanged(boolean cfi);
void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 2579d9f..a404a90 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -5,7 +5,6 @@
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
@@ -19,8 +18,6 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
-import java.util.concurrent.CountDownLatch;
-
/** @hide */
@SystemApi
public class RttManager {
@@ -311,7 +308,7 @@
}
public RttCapabilities getRttCapabilities() {
- synchronized (sCapabilitiesLock) {
+ synchronized (mCapabilitiesLock) {
if (mRttCapabilities == null) {
try {
mRttCapabilities = mService.getRttCapabilities();
@@ -932,13 +929,13 @@
validateChannel();
ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
Log.i(TAG, "Send RTT request to RTT Service");
- sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
+ mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
0, putListener(listener), parcelableParams);
}
public void stopRanging(RttListener listener) {
validateChannel();
- sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+ mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
}
/**
@@ -975,7 +972,7 @@
}
validateChannel();
int key = putListenerIfAbsent(callback);
- sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
}
/**
@@ -998,7 +995,7 @@
Log.e(TAG, "responder not enabled yet");
return;
}
- sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
}
/**
@@ -1110,23 +1107,17 @@
public static final int
CMD_OP_ENALBE_RESPONDER_FAILED = BASE + 8;
- private Context mContext;
- private IRttManager mService;
- private RttCapabilities mRttCapabilities;
-
private static final int INVALID_KEY = 0;
- private static int sListenerKey = 1;
- private static final SparseArray sListenerMap = new SparseArray();
- private static final Object sListenerMapLock = new Object();
- private static final Object sCapabilitiesLock = new Object();
+ private final Context mContext;
+ private final IRttManager mService;
+ private final SparseArray mListenerMap = new SparseArray();
+ private final Object mListenerMapLock = new Object();
+ private final Object mCapabilitiesLock = new Object();
- private static AsyncChannel sAsyncChannel;
- private static CountDownLatch sConnected;
-
- private static final Object sThreadRefLock = new Object();
- private static int sThreadRefCount;
- private static HandlerThread sHandlerThread;
+ private RttCapabilities mRttCapabilities;
+ private int mListenerKey = 1;
+ private AsyncChannel mAsyncChannel;
/**
* Create a new WifiScanner instance.
@@ -1135,122 +1126,107 @@
* the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
* @param context the application context
* @param service the Binder interface
+ * @param looper Looper for running the callbacks.
+ *
* @hide
*/
-
- public RttManager(Context context, IRttManager service) {
+ public RttManager(Context context, IRttManager service, Looper looper) {
mContext = context;
mService = service;
- init();
- }
-
- private void init() {
- synchronized (sThreadRefLock) {
- if (++sThreadRefCount == 1) {
- Messenger messenger = null;
- try {
- Log.d(TAG, "Get the messenger from " + mService);
- messenger = mService.getMessenger();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (SecurityException e) {
- /* do nothing */
- }
-
- if (messenger == null) {
- sAsyncChannel = null;
- return;
- }
-
- sHandlerThread = new HandlerThread("RttManager");
- sAsyncChannel = new AsyncChannel();
- sConnected = new CountDownLatch(1);
-
- sHandlerThread.start();
- Handler handler = new ServiceHandler(sHandlerThread.getLooper());
- sAsyncChannel.connect(mContext, handler, messenger);
- try {
- sConnected.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted wait at init");
- }
- }
+ Messenger messenger = null;
+ try {
+ Log.d(TAG, "Get the messenger from " + mService);
+ messenger = mService.getMessenger();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+
+ if (messenger == null) {
+ throw new IllegalStateException("getMessenger() returned null! This is invalid.");
+ }
+
+ mAsyncChannel = new AsyncChannel();
+
+ Handler handler = new ServiceHandler(looper);
+ mAsyncChannel.connectSync(mContext, handler, messenger);
+ // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
+ // synchronously, which causes RttService to receive the wrong replyTo value.
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
}
private void validateChannel() {
- if (sAsyncChannel == null) throw new IllegalStateException(
+ if (mAsyncChannel == null) throw new IllegalStateException(
"No permission to access and change wifi or a bad initialization");
}
- private static int putListener(Object listener) {
+ private int putListener(Object listener) {
if (listener == null) return INVALID_KEY;
int key;
- synchronized (sListenerMapLock) {
+ synchronized (mListenerMapLock) {
do {
- key = sListenerKey++;
+ key = mListenerKey++;
} while (key == INVALID_KEY);
- sListenerMap.put(key, listener);
+ mListenerMap.put(key, listener);
}
return key;
}
- // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener.
- private static int putListenerIfAbsent(Object listener) {
+ // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
+ private int putListenerIfAbsent(Object listener) {
if (listener == null) return INVALID_KEY;
- synchronized (sListenerMapLock) {
+ synchronized (mListenerMapLock) {
int key = getListenerKey(listener);
if (key != INVALID_KEY) {
return key;
}
do {
- key = sListenerKey++;
+ key = mListenerKey++;
} while (key == INVALID_KEY);
- sListenerMap.put(key, listener);
+ mListenerMap.put(key, listener);
return key;
}
}
- private static Object getListener(int key) {
+ private Object getListener(int key) {
if (key == INVALID_KEY) return null;
- synchronized (sListenerMapLock) {
- Object listener = sListenerMap.get(key);
+ synchronized (mListenerMapLock) {
+ Object listener = mListenerMap.get(key);
return listener;
}
}
- private static int getListenerKey(Object listener) {
+ private int getListenerKey(Object listener) {
if (listener == null) return INVALID_KEY;
- synchronized (sListenerMapLock) {
- int index = sListenerMap.indexOfValue(listener);
+ synchronized (mListenerMapLock) {
+ int index = mListenerMap.indexOfValue(listener);
if (index == -1) {
return INVALID_KEY;
} else {
- return sListenerMap.keyAt(index);
+ return mListenerMap.keyAt(index);
}
}
}
- private static Object removeListener(int key) {
+ private Object removeListener(int key) {
if (key == INVALID_KEY) return null;
- synchronized (sListenerMapLock) {
- Object listener = sListenerMap.get(key);
- sListenerMap.remove(key);
+ synchronized (mListenerMapLock) {
+ Object listener = mListenerMap.get(key);
+ mListenerMap.remove(key);
return listener;
}
}
- private static int removeListener(Object listener) {
+ private int removeListener(Object listener) {
int key = getListenerKey(listener);
if (key == INVALID_KEY) return key;
- synchronized (sListenerMapLock) {
- sListenerMap.remove(key);
+ synchronized (mListenerMapLock) {
+ mListenerMap.remove(key);
return key;
}
}
- private static class ServiceHandler extends Handler {
+ private class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@@ -1258,24 +1234,13 @@
public void handleMessage(Message msg) {
Log.i(TAG, "RTT manager get message: " + msg.what);
switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- } else {
- Log.e(TAG, "Failed to set up channel connection");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- sAsyncChannel = null;
- }
- return;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- sConnected.countDown();
return;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.e(TAG, "Channel connection lost");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
- sAsyncChannel = null;
+ mAsyncChannel = null;
getLooper().quit();
return;
}