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);
public abstract interface CharSequence {
method public abstract char charAt(int);
+ method public default java.util.stream.IntStream chars();
+ method public default java.util.stream.IntStream codePoints();
method public abstract int length();
method public abstract java.lang.CharSequence subSequence(int, int);
method public abstract java.lang.String toString();
method public final int arrayOffset();
method public abstract java.nio.CharBuffer asReadOnlyBuffer();
method public final char charAt(int);
+ method public java.util.stream.IntStream chars();
method public abstract java.nio.CharBuffer compact();
method public int compareTo(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer duplicate();
field public static final int IPCE_DHCP_BASE = 1024; // 0x400
field public static final int IPCE_DHCP_PARSE_ERROR = 1025; // 0x401
field public static final int IPCE_DHCP_RECV_ERROR = 1024; // 0x400
- field public static final int IPCE_DHCP_STATE_CHANGE = 1027; // 0x403
- field public static final int IPCE_DHCP_TIMEOUT = 1026; // 0x402
+ field public static final int IPCE_DHCP_STATE_CHANGE = 1026; // 0x402
field public static final int IPCE_DNS_BASE = 5120; // 0x1400
field public static final int IPCE_DNS_LOOKUPS = 5120; // 0x1400
field public static final int IPCE_IPMGR_BASE = 4096; // 0x1000
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);
public abstract interface CharSequence {
method public abstract char charAt(int);
+ method public default java.util.stream.IntStream chars();
+ method public default java.util.stream.IntStream codePoints();
method public abstract int length();
method public abstract java.lang.CharSequence subSequence(int, int);
method public abstract java.lang.String toString();
method public final int arrayOffset();
method public abstract java.nio.CharBuffer asReadOnlyBuffer();
method public final char charAt(int);
+ method public java.util.stream.IntStream chars();
method public abstract java.nio.CharBuffer compact();
method public int compareTo(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer duplicate();
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);
public abstract interface CharSequence {
method public abstract char charAt(int);
+ method public default java.util.stream.IntStream chars();
+ method public default java.util.stream.IntStream codePoints();
method public abstract int length();
method public abstract java.lang.CharSequence subSequence(int, int);
method public abstract java.lang.String toString();
method public final int arrayOffset();
method public abstract java.nio.CharBuffer asReadOnlyBuffer();
method public final char charAt(int);
+ method public java.util.stream.IntStream chars();
method public abstract java.nio.CharBuffer compact();
method public int compareTo(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer duplicate();
private void runStackStart() throws Exception {
String displayIdStr = nextArgRequired();
- int displayId = Integer.valueOf(displayIdStr);
+ int displayId = Integer.parseInt(displayIdStr);
Intent intent = makeIntent(UserHandle.USER_CURRENT);
try {
private void runStackMoveTask() throws Exception {
String taskIdStr = nextArgRequired();
- int taskId = Integer.valueOf(taskIdStr);
+ int taskId = Integer.parseInt(taskIdStr);
String stackIdStr = nextArgRequired();
- int stackId = Integer.valueOf(stackIdStr);
+ int stackId = Integer.parseInt(stackIdStr);
String toTopStr = nextArgRequired();
final boolean toTop;
if ("true".equals(toTopStr)) {
private void runStackResize() throws Exception {
String stackIdStr = nextArgRequired();
- int stackId = Integer.valueOf(stackIdStr);
+ int stackId = Integer.parseInt(stackIdStr);
final Rect bounds = getBounds();
if (bounds == null) {
System.err.println("Error: invalid input bounds");
private void runStackResizeAnimated() throws Exception {
String stackIdStr = nextArgRequired();
- int stackId = Integer.valueOf(stackIdStr);
+ int stackId = Integer.parseInt(stackIdStr);
final Rect bounds;
if ("null".equals(mArgs.peekNextArg())) {
bounds = null;
private void runStackPositionTask() throws Exception {
String taskIdStr = nextArgRequired();
- int taskId = Integer.valueOf(taskIdStr);
+ int taskId = Integer.parseInt(taskIdStr);
String stackIdStr = nextArgRequired();
- int stackId = Integer.valueOf(stackIdStr);
+ int stackId = Integer.parseInt(stackIdStr);
String positionStr = nextArgRequired();
- int position = Integer.valueOf(positionStr);
+ int position = Integer.parseInt(positionStr);
try {
mAm.positionTaskInStack(taskId, stackId, position);
private void runStackInfo() throws Exception {
try {
String stackIdStr = nextArgRequired();
- int stackId = Integer.valueOf(stackIdStr);
+ int stackId = Integer.parseInt(stackIdStr);
StackInfo info = mAm.getStackInfo(stackId);
System.out.println(info);
} catch (RemoteException e) {
private void runStackRemove() throws Exception {
String stackIdStr = nextArgRequired();
- int stackId = Integer.valueOf(stackIdStr);
+ int stackId = Integer.parseInt(stackIdStr);
mAm.removeStack(stackId);
}
private void runMoveTopActivityToPinnedStack() throws Exception {
- int stackId = Integer.valueOf(nextArgRequired());
+ int stackId = Integer.parseInt(nextArgRequired());
final Rect bounds = getBounds();
if (bounds == null) {
System.err.println("Error: invalid input bounds");
}
private void runStackSizeDockedStackTest() throws Exception {
- final int stepSize = Integer.valueOf(nextArgRequired());
+ final int stepSize = Integer.parseInt(nextArgRequired());
final String side = nextArgRequired();
final String delayStr = nextArg();
- final int delayMs = (delayStr != null) ? Integer.valueOf(delayStr) : 0;
+ final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
Rect bounds;
try {
if (taskIdStr.equals("stop")) {
mAm.stopLockTaskMode();
} else {
- int taskId = Integer.valueOf(taskIdStr);
+ int taskId = Integer.parseInt(taskIdStr);
mAm.startLockTaskMode(taskId);
}
System.err.println("Activity manager is " + (mAm.isInLockTaskMode() ? "" : "not ") +
private void runTaskResizeable() throws Exception {
final String taskIdStr = nextArgRequired();
- final int taskId = Integer.valueOf(taskIdStr);
+ final int taskId = Integer.parseInt(taskIdStr);
final String resizeableStr = nextArgRequired();
- final int resizeableMode = Integer.valueOf(resizeableStr);
+ final int resizeableMode = Integer.parseInt(resizeableStr);
try {
mAm.setTaskResizeable(taskId, resizeableMode);
private void runTaskResize() throws Exception {
final String taskIdStr = nextArgRequired();
- final int taskId = Integer.valueOf(taskIdStr);
+ final int taskId = Integer.parseInt(taskIdStr);
final Rect bounds = getBounds();
if (bounds == null) {
System.err.println("Error: invalid input bounds");
}
private void runTaskDragTaskTest() {
- final int taskId = Integer.valueOf(nextArgRequired());
- final int stepSize = Integer.valueOf(nextArgRequired());
+ final int taskId = Integer.parseInt(nextArgRequired());
+ final int stepSize = Integer.parseInt(nextArgRequired());
final String delayStr = nextArg();
- final int delay_ms = (delayStr != null) ? Integer.valueOf(delayStr) : 0;
+ final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
final StackInfo stackInfo;
Rect taskBounds;
try {
}
private void runTaskSizeTaskTest() {
- final int taskId = Integer.valueOf(nextArgRequired());
- final int stepSize = Integer.valueOf(nextArgRequired());
+ final int taskId = Integer.parseInt(nextArgRequired());
+ final int stepSize = Integer.parseInt(nextArgRequired());
final String delayStr = nextArg();
- final int delay_ms = (delayStr != null) ? Integer.valueOf(delayStr) : 0;
+ final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
final StackInfo stackInfo;
final Rect initialTaskBounds;
try {
private Rect getBounds() {
String leftStr = nextArgRequired();
- int left = Integer.valueOf(leftStr);
+ int left = Integer.parseInt(leftStr);
String topStr = nextArgRequired();
- int top = Integer.valueOf(topStr);
+ int top = Integer.parseInt(topStr);
String rightStr = nextArgRequired();
- int right = Integer.valueOf(rightStr);
+ int right = Integer.parseInt(rightStr);
String bottomStr = nextArgRequired();
- int bottom = Integer.valueOf(bottomStr);
+ int bottom = Integer.parseInt(bottomStr);
if (left < 0) {
System.err.println("Error: bad left arg: " + leftStr);
return null;
* 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);
}
}
private void handleBindApplication(AppBindData data) {
+ // Register the UI Thread as a sensitive thread to the runtime.
+ VMRuntime.registerSensitiveThread();
if (data.trackAllocation) {
DdmVmInternal.enableRecentAllocations(true);
}
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
* This may be used by the system to prioritize operations such as fragment lifecycle updates
* or loader ordering behavior.</p>
*
+ * <p><strong>Note:</strong> Prior to Android N there was a platform bug that could cause
+ * <code>setUserVisibleHint</code> to bring a fragment up to the started state before its
+ * <code>FragmentTransaction</code> had been committed. As some apps relied on this behavior,
+ * it is preserved for apps that declare a <code>targetSdkVersion</code> of 23 or lower.</p>
+ *
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
* false if it is not.
*/
public void setUserVisibleHint(boolean isVisibleToUser) {
- if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
- && mFragmentManager != null && isAdded()) {
+ // Prior to Android N we were simply checking if this fragment had a FragmentManager
+ // set before we would trigger a deferred start. Unfortunately this also gets set before
+ // a fragment transaction is committed, so if setUserVisibleHint was called before a
+ // transaction commit, we would start the fragment way too early. FragmentPagerAdapter
+ // triggers this situation.
+ // Unfortunately some apps relied on this timing in overrides of setUserVisibleHint
+ // on their own fragments, and expected, however erroneously, that after a call to
+ // super.setUserVisibleHint their onStart methods had been run.
+ // We preserve this behavior for apps targeting old platform versions below.
+ boolean useBrokenAddedCheck = false;
+ Context context = getContext();
+ if (mFragmentManager != null && mFragmentManager.mHost != null) {
+ context = mFragmentManager.mHost.getContext();
+ }
+ if (context != null) {
+ useBrokenAddedCheck = context.getApplicationInfo().targetSdkVersion <= VERSION_CODES.M;
+ }
+
+ final boolean performDeferredStart;
+ if (useBrokenAddedCheck) {
+ performDeferredStart = !mUserVisibleHint && isVisibleToUser && mState < STARTED
+ && mFragmentManager != null;
+ } else {
+ performDeferredStart = !mUserVisibleHint && isVisibleToUser && mState < STARTED
+ && mFragmentManager != null && isAdded();
+ }
+
+ if (performDeferredStart) {
mFragmentManager.performPendingDeferredStart(this);
}
+
mUserVisibleHint = isVisibleToUser;
mDeferStart = mState < STARTED && !isVisibleToUser;
}
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;
@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);
- }
+ if (DEBUG) {
+ Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+ + " with key=" + key);
+ }
- synchronized (this) {
- final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
- activityToken);
+ synchronized (this) {
+ final ActivityResources activityResources =
+ getOrCreateActivityResourcesStructLocked(
+ activityToken);
- if (overrideConfig != null) {
- activityResources.overrideConfig.setTo(overrideConfig);
- } else {
- activityResources.overrideConfig.setToDefaults();
+ if (overrideConfig != null) {
+ activityResources.overrideConfig.setTo(overrideConfig);
+ } else {
+ activityResources.overrideConfig.setToDefaults();
+ }
}
- }
- // Update any existing Activity Resources references.
- updateResourcesForActivity(activityToken, overrideConfig);
+ // Update any existing Activity Resources references.
+ updateResourcesForActivity(activityToken, overrideConfig);
- // Now request an actual Resources object.
- return getOrCreateResources(activityToken, key, classLoader);
+ // Now request an actual Resources object.
+ return getOrCreateResources(activityToken, key, classLoader);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
}
/**
}
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,
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
@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);
+ }
}
/**
*/
public void updateResourcesForActivity(@NonNull IBinder activityToken,
@Nullable Configuration overrideConfig) {
- 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;
+ 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;
}
- // 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);
+ 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);
}
- // 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);
+ 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 (DEBUG) {
- Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
- + " to newKey=" + newKey);
- }
+ // 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;
+ }
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
- if (resourcesImpl == null) {
- resourcesImpl = createResourcesImpl(newKey);
- mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
- }
+ // 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);
+ }
- if (resourcesImpl != resources.getImpl()) {
- // Set the ResourcesImpl, updating it for all users of this Resources object.
- resources.setImpl(resourcesImpl);
+ 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);
}
}
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();
-
- if (compat != null && (mResCompatibilityInfo == null ||
- !mResCompatibilityInfo.equals(compat))) {
- mResCompatibilityInfo = compat;
- changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_SCREEN_SIZE
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+ "ResourcesManager#applyConfigurationToResourcesLocked");
+
+ 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();
+
+ 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;
+ 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);
-
- ApplicationPackageManager.configurationChanged();
- //Slog.i(TAG, "Configuration changed in " + currentPackageName());
-
- 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();
- }
- tmpConfig.setTo(localeAdjustedConfig);
- if (!isDefaultDisplay) {
- dm = getDisplayMetrics(displayId);
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
+ Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
+ compat);
+
+ ApplicationPackageManager.configurationChanged();
+ //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+
+ 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();
+ }
+ 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);
}
- 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);
+ }
}
}
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,
* @return if the record is valid
*/
public boolean isValid() {
- return ((mControllerTxTimeMs !=0) ||
- (mControllerRxTimeMs !=0) ||
- (mControllerIdleTimeMs !=0));
+ return ((mControllerTxTimeMs >=0) &&
+ (mControllerRxTimeMs >=0) &&
+ (mControllerIdleTimeMs >=0));
}
}
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.SynchronousResultReceiver;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Pair;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.TimeoutException;
/**
* Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
*
* @return a record with {@link BluetoothActivityEnergyInfo} or null if
* report is unavailable or unsupported
+ * @deprecated use the asynchronous
+ * {@link #requestControllerActivityEnergyInfo(int, ResultReceiver)} instead.
* @hide
*/
+ @Deprecated
public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
- if (getState() != STATE_ON) return null;
+ SynchronousResultReceiver receiver = new SynchronousResultReceiver();
+ requestControllerActivityEnergyInfo(updateType, receiver);
+ try {
+ SynchronousResultReceiver.Result result = receiver.awaitResult(1000);
+ if (result.bundle != null) {
+ return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
+ }
+ } catch (TimeoutException e) {
+ Log.e(TAG, "getControllerActivityEnergyInfo timed out");
+ }
+ return null;
+ }
+
+ /**
+ * Request the record of {@link BluetoothActivityEnergyInfo} object that
+ * has the activity and energy info. This can be used to ascertain what
+ * the controller has been up to, since the last sample.
+ *
+ * A null value for the activity info object may be sent if the bluetooth service is
+ * unreachable or the device does not support reporting such information.
+ *
+ * @param updateType Type of info, cached vs refreshed.
+ * @param result The callback to which to send the activity info.
+ * @hide
+ */
+ public void requestControllerActivityEnergyInfo(int updateType, ResultReceiver result) {
+ if (getState() != STATE_ON) {
+ result.send(0, null);
+ return;
+ }
+
try {
- BluetoothActivityEnergyInfo record;
if (!mService.isActivityAndEnergyReportingSupported()) {
- return null;
+ result.send(0, null);
+ return;
}
synchronized(this) {
if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) {
mService.getActivityEnergyInfoFromController();
wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS);
}
- record = mService.reportActivityInfo();
- if (record.isValid()) {
- return record;
- } else {
- return null;
- }
+ mService.requestActivityInfo(result);
}
} catch (InterruptedException e) {
Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e);
+ result.send(0, null);
} catch (RemoteException e) {
Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
+ result.send(0, null);
}
- return null;
}
/**
import android.bluetooth.OobData;
import android.os.ParcelUuid;
import android.os.ParcelFileDescriptor;
+import android.os.ResultReceiver;
/**
* System private API for talking with the Bluetooth service.
void getActivityEnergyInfoFromController();
BluetoothActivityEnergyInfo reportActivityInfo();
+ /**
+ * Requests the controller activity info asynchronously.
+ * The implementor is expected to reply with the
+ * {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the
+ * key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}.
+ * The result code is ignored.
+ */
+ oneway void requestActivityInfo(in ResultReceiver result);
+
void onLeServiceUp();
void onBrEdrDown();
}
} else if (ATTR_COMPONENT.equals(attrName)) {
intent.setComponent(ComponentName.unflattenFromString(attrValue));
} else if (ATTR_FLAGS.equals(attrName)) {
- intent.setFlags(Integer.valueOf(attrValue, 16));
+ intent.setFlags(Integer.parseInt(attrValue, 16));
} else {
Log.e("Intent", "restoreFromXml: unknown attribute=" + attrName);
}
/**
* 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
" currently connected camera device", cameraId));
}
- int id = Integer.valueOf(cameraId);
+ int id = Integer.parseInt(cameraId);
/*
* Get the camera characteristics from the camera service directly if it supports it,
package android.hardware.input;
+import java.util.Objects;
+
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
/**
* Wrapper for passing identifying information for input devices.
return mProductId;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof InputDeviceIdentifier)) return false;
+
+ final InputDeviceIdentifier that = (InputDeviceIdentifier) o;
+ return ((mVendorId == that.mVendorId) && (mProductId == that.mProductId)
+ && TextUtils.equals(mDescriptor, that.mDescriptor));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDescriptor, mVendorId, mProductId);
+ }
+
public static final Parcelable.Creator<InputDeviceIdentifier> CREATOR =
new Parcelable.Creator<InputDeviceIdentifier>() {
}
public static void logReceiveError(String ifName) {
- IpConnectivityEvent.logEvent(IPCE_DHCP_RECV_ERROR,
- new DhcpErrorEvent(ifName, RECEIVE_ERROR));
+ logEvent(IPCE_DHCP_RECV_ERROR, new DhcpErrorEvent(ifName, RECEIVE_ERROR));
}
public static int errorCodeWithOption(int errorCode, int option) {
public static final int IPCE_DHCP_RECV_ERROR = IPCE_DHCP_BASE + 0;
public static final int IPCE_DHCP_PARSE_ERROR = IPCE_DHCP_BASE + 1;
- public static final int IPCE_DHCP_TIMEOUT = IPCE_DHCP_BASE + 2;
- public static final int IPCE_DHCP_STATE_CHANGE = IPCE_DHCP_BASE + 3;
+ public static final int IPCE_DHCP_STATE_CHANGE = IPCE_DHCP_BASE + 2;
public static final int IPCE_NETMON_STATE_CHANGE = IPCE_NETMON_BASE + 0;
public static final int IPCE_NETMON_CHECK_RESULT = IPCE_NETMON_BASE + 1;
return false;
}
try {
- Integer.valueOf(systemCode, 16);
+ Integer.parseInt(systemCode, 16);
} catch (NumberFormatException e) {
Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
return false;
private static final String VIDEO_DATA = "vid";
private static final String AUDIO_DATA = "aud";
+ public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+
private final StringBuilder mFormatBuilder = new StringBuilder(32);
private final Formatter mFormatter = new Formatter(mFormatBuilder);
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;
}
String policyString = message.substring(7, spaceIndex);
try {
- return Integer.valueOf(policyString).intValue();
+ return Integer.parseInt(policyString);
} catch (NumberFormatException e) {
return 0;
}
}
String violationString = message.substring(numberStartIndex, numberEndIndex);
try {
- return Integer.valueOf(violationString).intValue();
+ return Integer.parseInt(violationString);
} catch (NumberFormatException e) {
return 0;
}
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Extends ResultReceiver to allow the server end of the ResultReceiver to synchronously wait
+ * on the response from the client. This enables an RPC like system but with the ability to
+ * timeout and discard late results.
+ *
+ * NOTE: Can only be used for one response. Subsequent responses on the same instance are ignored.
+ * {@hide}
+ */
+public class SynchronousResultReceiver extends ResultReceiver {
+ public static class Result {
+ public int resultCode;
+ @Nullable public Bundle bundle;
+
+ public Result(int resultCode, @Nullable Bundle bundle) {
+ this.resultCode = resultCode;
+ this.bundle = bundle;
+ }
+ }
+
+ private final CompletableFuture<Result> mFuture = new CompletableFuture<>();
+
+ public SynchronousResultReceiver() {
+ super((Handler) null);
+ }
+
+ @Override
+ final protected void onReceiveResult(int resultCode, Bundle resultData) {
+ super.onReceiveResult(resultCode, resultData);
+ mFuture.complete(new Result(resultCode, resultData));
+ }
+
+ /**
+ * Blocks waiting for the result from the remote client.
+ *
+ * @return the Result
+ * @throws TimeoutException if the timeout in milliseconds expired.
+ */
+ public @NonNull Result awaitResult(long timeoutMillis) throws TimeoutException {
+ final long deadline = System.currentTimeMillis() + timeoutMillis;
+ while (timeoutMillis >= 0) {
+ try {
+ return mFuture.get(timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (ExecutionException e) {
+ // This will NEVER happen.
+ throw new AssertionError("Error receiving response", e);
+ } catch (InterruptedException e) {
+ // The thread was interrupted, try and get the value again, this time
+ // with the remaining time until the deadline.
+ timeoutMillis -= deadline - System.currentTimeMillis();
+ }
+ }
+ throw new TimeoutException();
+ }
+
+}
}
@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();
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;
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();
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;
}
}
/** {@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);
private static int tryParseInt(String value, int defValue) {
if (TextUtils.isEmpty(value)) return defValue;
try {
- return Integer.valueOf(value);
+ return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defValue;
}
package android.view;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.CanvasProperty;
* 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);
}
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.
* @param labelRes The resource ID the application would like to use as its name.
* @param icon The resource ID the application would like to use as its icon.
* @param windowFlags Window layout flags.
+ * @param overrideConfig override configuration to consider when generating
+ * context to for resources.
*
* @return Optionally you can return the View that was used to create the
* window, for easy removal in removeStartingWindow.
*/
public View addStartingWindow(IBinder appToken, String packageName,
int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
- int labelRes, int icon, int logo, int windowFlags);
+ int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig);
/**
* Called when the first window of an application has been displayed, while
import com.android.internal.os.BatteryStatsImpl;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.net.wifi.WifiActivityEnergyInfo;
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength;
interface IBatteryStats {
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
+
+ oneway void noteBluetoothControllerActivity(in BluetoothActivityEnergyInfo info);
+ oneway void noteModemControllerActivity(in ModemActivityInfo info);
+ oneway void noteWifiControllerActivity(in WifiActivityEnergyInfo info);
}
private int getInt(final String key, final int defaultValue) {
if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
final String result = mCopyOnWriteDataStore.get(key);
- return result != null ? Integer.valueOf(result) : 0;
+ return result != null ? Integer.parseInt(result) : 0;
}
return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
}
if (s.equals(subtypeHashCode)) {
// If both imeId and subtypeId are enabled, return subtypeId.
try {
- final int hashCode = Integer.valueOf(subtypeHashCode);
+ final int hashCode = Integer.parseInt(subtypeHashCode);
// Check whether the subtype id is valid or not
if (isValidSubtypeId(imi, hashCode)) {
return s;
VpnProfile profile = new VpnProfile(key);
profile.name = values[0];
- profile.type = Integer.valueOf(values[1]);
+ profile.type = Integer.parseInt(values[1]);
if (profile.type < 0 || profile.type > TYPE_MAX) {
return null;
}
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.
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) {
}
return super.getSystemService(name);
}
+
+ @Override
+ public Resources getResources() {
+ return mActivityResources;
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ return mActivityResources.getAssets();
+ }
}
if (applicationContext == null) {
context = getContext();
} else {
- context = new DecorContext(applicationContext);
+ context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
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());
}
auto functor = std::bind(
std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this,
- (jlong) info.frameNumber, (jint) bounds.left, (jint) bounds.top,
+ (jlong) info.canvasContext.getFrameNumber(),
+ (jint) bounds.left, (jint) bounds.top,
(jint) bounds.right, (jint) bounds.bottom);
info.canvasContext.enqueueFrameWork(std::move(functor));
@BeforeExperiment
protected void setUp() throws Exception {
clazz = Class.forName(paramType);
- int strSize = Integer.valueOf(paramStringMult);
+ int strSize = Integer.parseInt(paramStringMult);
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < strSize; i++) {
strBuilder.append(TEST_STRING);
@BeforeExperiment
protected void setUp() throws Exception {
- int strSize = Integer.valueOf(paramStringMult);
+ int strSize = Integer.parseInt(paramStringMult);
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < strSize; i++) {
strBuilder.append(SpannableStringBuilderBenchmark.TEST_STRING);
@BeforeExperiment
protected void setUp() throws Exception {
- int copyAmount = Integer.valueOf(mParamCopyAmount);
+ int copyAmount = Integer.parseInt(mParamCopyAmount);
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < copyAmount; i++) {
strBuilder.append(mParamBasicText);
#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>
_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;
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());
if (idmap != NULL) {
delete idmap;
}
- MY_TRACE_END();
-
return onlyEmptyResources;
}
void AssetManager::updateResourceParamsLocked() const
{
+ ATRACE_CALL();
ResTable* res = mResources;
if (!res) {
return;
#include <stdlib.h>
#include <string.h>
+#include <algorithm>
#include <limits>
#include <memory>
#include <type_traits>
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();
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));
}
}
}
}
}
+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;
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));
}
}
}
*/
#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));
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
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) {
};
}
+namespace integer {
+ enum {
+ number = 0x01030000, // sv
+ };
+}
+
} // namespace R
} // namespace android
--- /dev/null
+<?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>
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,
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,
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;
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));
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--) {
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
//
// 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);
-
- 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);
+
+ // Backdrop bounds in render target space
+ const Rect backdrop = nodeBounds(*nodes[0]);
+
+ // 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]);
}
+ }
+
+ if (!backdrop.isEmpty()) {
+ // content node translation to catch up with backdrop
+ float dx = contentDrawBounds.left - backdrop.left;
+ float dy = contentDrawBounds.top - backdrop.top;
- deferNodePropsAndOps(*node);
- mCanvasState.restoreToCount(count);
- layer++;
+ 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]);
+ }
}
}
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
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() {}
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
* 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;
float mLightRadius;
- // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
- LinearAllocator mAllocator;
-
const bool mDrawFbo0;
};
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();
}
// tree state changes
TreeObserver* observer = nullptr;
- // Frame number for use with synchronized surfaceview position updating
- int64_t frameNumber = -1;
int32_t windowInsetLeft = 0;
int32_t windowInsetTop = 0;
bool updateWindowPositions = false;
mEglSurface = mEglManager.createSurface(surface);
}
+ mFrameNumber = -1;
+
if (mEglSurface != EGL_NO_SURFACE) {
const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
info.renderer = mCanvas;
#endif
- if (CC_LIKELY(mNativeSurface.get())) {
- info.frameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber());
- }
-
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
// Only the primary target node will be drawn full - all other nodes would get drawn in
#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);
swap.swapTime = systemTime(CLOCK_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
mHaveNewSurface = false;
+ mFrameNumber = -1;
}
// TODO: Use a fence for real completion?
#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);
mFrameWorkProcessor->add(task);
}
+int64_t CanvasContext::getFrameNumber() {
+ // mFrameNumber is reset to -1 when the surface changes or we swap buffers
+ if (mFrameNumber == -1 && mNativeSurface.get()) {
+ mFrameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber());
+ }
+ return mFrameNumber;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
// Used to queue up work that needs to be completed before this frame completes
ANDROID_API void enqueueFrameWork(std::function<void()>&& func);
+ ANDROID_API int64_t getFrameNumber();
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
};
RingBuffer<SwapHistory, 3> mSwapHistory;
+ int64_t mFrameNumber = -1;
bool mOpaque;
#if HWUI_NEW_OPS
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;
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);
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);
}
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;
});
TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
- std::vector<sp<RenderNode>> nodes;
- nodes.emplace_back(rootNode);
- return nodes;
+ return rootNode;
}
static auto SCENES = {
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);
}
});
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);
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.
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
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());
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);
}
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:
}
};
- // 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);
}
[](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,"
// 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";
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";
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());
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";
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())
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";
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());
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());
[&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);
}
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";
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";
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());
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);
}
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());
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());
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
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());
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())
});
// 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());
});
// 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);
}
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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";
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(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*syncedNode);
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
- nodes, sLightGeometry, Caches::getInstance());
SaveLayerAlphaClipTestRenderer renderer(outObservedData);
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
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());
using namespace android;
using namespace android::uirenderer;
-const LayerUpdateQueue sEmptyLayerUpdateQueue;
const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
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);
}
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);
}
mContext = context;
mServiceComponent = serviceComponent;
mCallback = callback;
- mRootHints = rootHints;
+ mRootHints = rootHints == null ? null : new Bundle(rootHints);
}
/**
}
};
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() {
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);
}
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;
}
@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;
}
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);
}
});
}
}
/**
+ * 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.
}
};
+ 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()"
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
}
};
- MediaBrowserService.this.onLoadItem(itemId, result);
+ mCurConnection = connection;
+ onLoadItem(itemId, result);
+ mCurConnection = null;
if (!result.isDone()) {
throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
List<Range<Integer> > fpsRanges = Arrays.asList(
mStaticInfo.getAeAvailableTargetFpsRangesChecked());
- int cameraId = Integer.valueOf(mCamera.getId());
+ int cameraId = Integer.parseInt(mCamera.getId());
int maxVideoFrameRate = -1;
for (int profileId : camcorderProfileList) {
if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
int kFrameDrop_Tolerence = FRAMEDROP_TOLERANCE;
for (int profileId : mCamcorderProfileList) {
- int cameraId = Integer.valueOf(mCamera.getId());
+ int cameraId = Integer.parseInt(mCamera.getId());
if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
allowedUnsupported(cameraId, profileId)) {
continue;
break;
}
}
+ if (internalRoot == null) {
+ // Should not happen on normal circumstances, unless app crafted an invalid volume
+ // using reflection or the list of mounted volumes changed.
+ Log.e(TAG, "Didn't find right volume for '" + storageVolume.dump() + "' on " + volumes);
+ return false;
+ }
// Checks if the user has granted the permission already.
final Intent intent = getIntentForExistingPermission(activity, isRoot, internalRoot, file);
<!-- [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">
launchBugreportInfoDialog(id);
break;
case INTENT_BUGREPORT_SCREENSHOT:
- takeScreenshot(id, true);
+ takeScreenshot(id);
break;
case INTENT_BUGREPORT_SHARE:
shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
return true;
}
mProcesses.put(info.id, info);
- // Take initial screenshot.
- takeScreenshot(id, false);
updateProgress(info);
return true;
}
/**
* 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
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();
-
- takeScreenshot(id, SCREENSHOT_DELAY_SECONDS);
- } else {
- takeScreenshot(id, 0);
- }
+ 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);
}
/**
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();
}
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();
}
resetProperties();
sendBugreportStarted(1000);
+ waitForScreenshotButtonEnabled(true);
+ takeScreenshot();
sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
waitShareNotification(ID);
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();
}
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();
}
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();
}
// 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();
}
// 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();
}
--- /dev/null
+<?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
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
--- /dev/null
+<?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
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/brightness_mirror"
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:visibility="invisible">
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/brightness_mirror_background"
+ android:elevation="2dp">
+ <include layout="@layout/quick_settings_brightness_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+</FrameLayout>
android:id="@+id/keyboard_shortcuts_icon"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_marginEnd="32dp"
+ android:layout_marginEnd="24dp"
android:layout_gravity="center_vertical"
android:visibility="gone"
android:layout_alignParentStart="true"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="0dp"
- android:layout_marginBottom="20dp"
+ android:layout_marginBottom="0dp"
android:background="?android:attr/dividerHorizontal" />
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="14sp"
+ android:fontFamily="sans-serif-medium"
android:paddingStart="24dp"
android:paddingTop="20dp"
android:paddingEnd="24dp"
- android:paddingBottom="13dp"/>
+ android:paddingBottom="10dp"/>
android:shadowRadius="5"
android:fontFamily="sans-serif-medium"
android:background="?android:selectableItemBackground"
- android:visibility="invisible" />
+ android:visibility="invisible"
+ android:forceHasOverlappingRendering="false" />
<!-- extends LinearLayout -->
<com.android.systemui.statusbar.SignalClusterView
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/signal_cluster"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height" />
- <FrameLayout android:id="@+id/brightness_mirror"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="wrap_content"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:visibility="invisible">
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:elevation="2dp"
- android:background="@drawable/brightness_mirror_background">
- <include layout="@layout/quick_settings_brightness_dialog"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
- </FrameLayout>
+ <include layout="@layout/brightness_mirror" />
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout="@layout/car_fullscreen_user_switcher"
android:orientation="horizontal"/>
<include layout="@layout/signal_cluster_view"
- android:id="@+id/signal_cluster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
<!-- The amount of overscroll allowed when flinging to the end of the stack. -->
<dimen name="recents_fling_overscroll_distance">24dp</dimen>
+ <!-- The size of the drag hint text. -->
+ <dimen name="recents_drag_hint_text_size">14sp</dimen>
+
<!-- The min alpha to apply to a task affiliation group color. -->
<item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
<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>
<string name="recents_stack_action_button_label">Clear all</string>
<!-- Recents: Incompatible task message. [CHAR LIMIT=NONE] -->
<string name="recents_incompatible_app_message">App doesn\'t support split screen</string>
+ <!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
+ <string name="recents_drag_hint_message">Drag here to use split screen</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
- private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
+ private int MAX_DISMISS_VELOCITY = 4000; // dp/sec
private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
- public static float SWIPE_PROGRESS_FADE_START = 0f; // fraction of thumbnail width
- // where fade starts
static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
// beyond which swipe progress->0
private float mMinSwipeProgress = 0f;
mFalsingThreshold = context.getResources().getDimensionPixelSize(
R.dimen.swipe_helper_falsing_threshold);
mFalsingManager = FalsingManager.getInstance(context);
- mFlingAnimationUtils = new FlingAnimationUtils(context,
- MAX_ESCAPE_ANIMATION_DURATION / 1000f /* maxLengthSeconds */);
+ mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
}
public void setLongPressListener(LongPressListener listener) {
mMaxSwipeProgress = maxSwipeProgress;
}
- private float getSwipeProgressForOffset(View view) {
+ private float getSwipeProgressForOffset(View view, float translation) {
float viewSize = getSize(view);
- final float fadeSize = SWIPE_PROGRESS_FADE_END * viewSize;
- float result = 1.0f;
- float pos = getTranslation(view);
- if (pos >= viewSize * SWIPE_PROGRESS_FADE_START) {
- result = 1.0f - (pos - viewSize * SWIPE_PROGRESS_FADE_START) / fadeSize;
- } else if (pos < viewSize * (1.0f - SWIPE_PROGRESS_FADE_START)) {
- result = 1.0f + (viewSize * SWIPE_PROGRESS_FADE_START + pos) / fadeSize;
- }
+ float result = Math.abs(translation / viewSize);
return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
}
+ private float getSwipeAlpha(float progress) {
+ return Math.min(0, Math.max(1, progress / SWIPE_PROGRESS_FADE_END));
+ }
+
private void updateSwipeProgressFromOffset(View animView, boolean dismissable) {
- float swipeProgress = getSwipeProgressForOffset(animView);
+ updateSwipeProgressFromOffset(animView, dismissable, getTranslation(animView));
+ }
+
+ private void updateSwipeProgressFromOffset(View animView, boolean dismissable,
+ float translation) {
+ float swipeProgress = getSwipeProgressForOffset(animView, translation);
if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
if (FADE_OUT_DURING_SWIPE && dismissable) {
float alpha = swipeProgress;
animView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
- animView.setAlpha(getSwipeProgressForOffset(animView));
+ animView.setAlpha(getSwipeAlpha(swipeProgress));
}
}
invalidateGlobalRegion(animView);
* view is being animated to dismiss or snap.
*/
public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
- updateSwipeProgressFromOffset(animView, canBeDismissed);
+ updateSwipeProgressFromOffset(animView, canBeDismissed, value);
}
private void snapChildInstantly(final View view) {
}
protected float getEscapeVelocity() {
- return SWIPE_ESCAPE_VELOCITY * mDensityScale;
+ return getUnscaledEscapeVelocity() * mDensityScale;
+ }
+
+ protected float getUnscaledEscapeVelocity() {
+ return SWIPE_ESCAPE_VELOCITY;
+ }
+
+ protected long getMaxEscapeAnimDuration() {
+ return MAX_ESCAPE_ANIMATION_DURATION;
}
protected boolean swipedFarEnough() {
}
ImageView first = (ImageView) getChildAt(firstIndex);
ImageView second = (ImageView) getChildAt(secondIndex);
- if (second == null) {
- // Weird state where number of pages must not have propagated yet.
- return;
+ if (first == null || second == null) {
+ // may happen during reInflation or other weird cases
}
// Lay the two views on top of each other.
second.setTranslationX(first.getX() - second.getX());
private RecentsPackageMonitor mPackageMonitor;
private long mLastTabKeyEventTime;
private int mLastDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
+ private int mLastDisplayDensity;
private boolean mFinishedOnStartup;
private boolean mIgnoreAltTabRelease;
private boolean mIsVisible;
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
- mLastDeviceOrientation = Utilities.getAppConfiguration(this).orientation;
+ Configuration appConfiguration = Utilities.getAppConfiguration(this);
+ mLastDeviceOrientation = appConfiguration.orientation;
+ mLastDisplayDensity = appConfiguration.densityDpi;
mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
@Override
loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
- mRecentsView.updateStack(stack);
+ mRecentsView.updateStack(stack, true /* setStackViewTasks */);
// Update the nav bar scrim, but defer the animation until the enter-window event
boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
super.onConfigurationChanged(newConfig);
// Notify of the config change
- int newDeviceOrientation = Utilities.getAppConfiguration(this).orientation;
+ Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
int numStackTasks = mRecentsView.getStack().getStackTaskCount();
EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
- (mLastDeviceOrientation != newDeviceOrientation), numStackTasks > 0));
- mLastDeviceOrientation = newDeviceOrientation;
+ mLastDeviceOrientation != newDeviceConfiguration.orientation,
+ mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0));
+ mLastDeviceOrientation = newDeviceConfiguration.orientation;
+ mLastDisplayDensity = newDeviceConfiguration.densityDpi;
}
@Override
int numStackTasks = stack.getStackTaskCount();
EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
- false /* fromDeviceOrientationChange */, numStackTasks > 0));
-
- if (mRecentsView != null) {
- mRecentsView.updateStack(stack);
- }
-
- EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
+ false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */,
numStackTasks > 0));
+ EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, stack));
}
@Override
com.android.internal.R.dimen.navigation_bar_height);
mNavBarWidth = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_width);
- mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height,
if (icon != null) {
icon.setCallback(null);
}
- mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
+ mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
disabledInSafeMode);
+ mHeaderBar.onTaskDataLoaded();
mHeaderBar.setDimAlpha(toTransform.dimAlpha);
mHeaderBar.draw(c);
c.setBitmap(null);
public final boolean fromMultiWindow;
public final boolean fromDeviceOrientationChange;
+ public final boolean fromDisplayDensityChange;
public final boolean hasStackTasks;
public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromDeviceOrientationChange,
- boolean hasStackTasks) {
+ boolean fromDisplayDensityChange, boolean hasStackTasks) {
this.fromMultiWindow = fromMultiWindow;
this.fromDeviceOrientationChange = fromDeviceOrientationChange;
+ this.fromDisplayDensityChange = fromDisplayDensityChange;
this.hasStackTasks = hasStackTasks;
}
}
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.TaskStack;
/**
* This is sent by the activity whenever the multi-window state has changed.
*/
-public class MultiWindowStateChangedEvent extends EventBus.Event {
+public class MultiWindowStateChangedEvent extends EventBus.AnimatedEvent {
public final boolean inMultiWindow;
- public final boolean hasStackTasks;
+ public final TaskStack stack;
- public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean hasStackTasks) {
+ public MultiWindowStateChangedEvent(boolean inMultiWindow, TaskStack stack) {
this.inMultiWindow = inMultiWindow;
- this.hasStackTasks = hasStackTasks;
+ this.stack = stack;
}
}
}
/**
- * Returns the current display orientation.
- */
- public int getDisplayOrientation() {
- // Because of multi-window, the configuration orientation does not necessarily reflect the
- // orientation of the display, instead we just use the display's real-size.
- Rect displayRect = getDisplayRect();
- return displayRect.width() > displayRect.height()
- ? Configuration.ORIENTATION_LANDSCAPE
- : Configuration.ORIENTATION_PORTRAIT;
- }
-
- /**
* Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
*/
public Rect getWindowRect() {
/**
* Acquires the task resource data directly from the cache, loading if necessary.
- *
- * @param fetchAndInvalidateThumbnails If set, will try loading thumbnails, invalidating them
- * in the cache and loading if necessary. Otherwise, do not
- * load the thumbnail unless the icon also has to be loaded.
*/
- public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
+ public void loadTaskData(Task t) {
Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
Bitmap thumbnail = null;
ActivityManager.TaskThumbnailInfo thumbnailInfo = null;
- if (fetchAndInvalidateThumbnails) {
- ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
- if (thumbnailData != null) {
- thumbnail = thumbnailData.thumbnail;
- thumbnailInfo = thumbnailData.thumbnailInfo;
- }
- } else {
- thumbnail = mDefaultThumbnail;
+ ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
+ if (thumbnailData != null) {
+ thumbnail = thumbnailData.thumbnail;
+ thumbnailInfo = thumbnailData.thumbnailInfo;
}
// Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntProperty;
import android.util.SparseArray;
import android.view.animation.Interpolator;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
*/
public static class DockState implements DropTarget {
+ // The rotation to apply to the hint text
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HORIZONTAL, VERTICAL})
+ public @interface TextOrientation {}
+ private static final int HORIZONTAL = 0;
+ private static final int VERTICAL = 1;
+
private static final int DOCK_AREA_ALPHA = 192;
- public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, null, null, null);
+ public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
+ null, null, null);
public static final DockState LEFT = new DockState(DOCKED_LEFT,
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
+ DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
new RectF(0, 0, 0.5f, 1));
public static final DockState TOP = new DockState(DOCKED_TOP,
- DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
+ DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
new RectF(0, 0, 1, 0.5f));
public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
- DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
+ DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
new RectF(0.5f, 0, 1, 1));
public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
- DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
+ DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
new RectF(0, 0.5f, 1, 1));
}
// Represents the view state of this dock state
- public class ViewState {
+ public static class ViewState {
+ private static final IntProperty<ViewState> HINT_ALPHA =
+ new IntProperty<ViewState>("drawableAlpha") {
+ @Override
+ public void setValue(ViewState object, int alpha) {
+ object.mHintTextAlpha = alpha;
+ object.dockAreaOverlay.invalidateSelf();
+ }
+
+ @Override
+ public Integer get(ViewState object) {
+ return object.mHintTextAlpha;
+ }
+ };
+
public final int dockAreaAlpha;
public final ColorDrawable dockAreaOverlay;
- private AnimatorSet dockAreaOverlayAnimator;
-
- private ViewState(int alpha) {
- dockAreaAlpha = alpha;
+ public final int hintTextAlpha;
+ public final int hintTextOrientation;
+
+ private final int mHintTextResId;
+ private String mHintText;
+ private Paint mHintTextPaint;
+ private Point mHintTextBounds = new Point();
+ private int mHintTextAlpha = 255;
+ private AnimatorSet mDockAreaOverlayAnimator;
+ private Rect mTmpRect = new Rect();
+
+ private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
+ int hintTextResId) {
+ dockAreaAlpha = areaAlpha;
dockAreaOverlay = new ColorDrawable(0xFFffffff);
dockAreaOverlay.setAlpha(0);
+ hintTextAlpha = hintAlpha;
+ hintTextOrientation = hintOrientation;
+ mHintTextResId = hintTextResId;
+ mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mHintTextPaint.setColor(Color.WHITE);
+ }
+
+ /**
+ * Updates the view state with the given context.
+ */
+ public void update(Context context) {
+ Resources res = context.getResources();
+ mHintText = context.getString(mHintTextResId);
+ mHintTextPaint.setTextSize(res.getDimensionPixelSize(
+ R.dimen.recents_drag_hint_text_size));
+ mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
+ mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
+ }
+
+ /**
+ * Draws the current view state.
+ */
+ public void draw(Canvas canvas) {
+ // Draw the overlay background
+ if (dockAreaOverlay.getAlpha() > 0) {
+ dockAreaOverlay.draw(canvas);
+ }
+
+ // Draw the hint text
+ if (mHintTextAlpha > 0) {
+ Rect bounds = dockAreaOverlay.getBounds();
+ int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
+ int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
+ mHintTextPaint.setAlpha(mHintTextAlpha);
+ if (hintTextOrientation == VERTICAL) {
+ canvas.save();
+ canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
+ }
+ canvas.drawText(mHintText, x, y, mHintTextPaint);
+ if (hintTextOrientation == VERTICAL) {
+ canvas.restore();
+ }
+ }
}
/**
* Creates a new bounds and alpha animation.
*/
- public void startAnimation(Rect bounds, int alpha, int duration,
+ public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
- if (dockAreaOverlayAnimator != null) {
- dockAreaOverlayAnimator.cancel();
+ if (mDockAreaOverlayAnimator != null) {
+ mDockAreaOverlayAnimator.cancel();
}
ArrayList<Animator> animators = new ArrayList<>();
- if (dockAreaOverlay.getAlpha() != alpha) {
+ if (dockAreaOverlay.getAlpha() != areaAlpha) {
if (animateAlpha) {
animators.add(ObjectAnimator.ofInt(dockAreaOverlay,
- Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), alpha));
+ Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha));
} else {
- dockAreaOverlay.setAlpha(alpha);
+ dockAreaOverlay.setAlpha(areaAlpha);
+ }
+ }
+ if (mHintTextAlpha != hintAlpha) {
+ if (animateAlpha) {
+ animators.add(ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
+ hintAlpha));
+ } else {
+ mHintTextAlpha = hintAlpha;
+ dockAreaOverlay.invalidateSelf();
}
}
if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
}
}
if (!animators.isEmpty()) {
- dockAreaOverlayAnimator = new AnimatorSet();
- dockAreaOverlayAnimator.playTogether(animators);
- dockAreaOverlayAnimator.setDuration(duration);
- dockAreaOverlayAnimator.setInterpolator(interpolator);
- dockAreaOverlayAnimator.start();
+ mDockAreaOverlayAnimator = new AnimatorSet();
+ mDockAreaOverlayAnimator.playTogether(animators);
+ mDockAreaOverlayAnimator.setDuration(duration);
+ mDockAreaOverlayAnimator.setInterpolator(interpolator);
+ mDockAreaOverlayAnimator.start();
}
}
}
* the initial touch area. This is also the new dock area to
* draw.
*/
- DockState(int dockSide, int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
+ DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
+ @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
RectF expandedTouchDockArea) {
this.dockSide = dockSide;
this.createMode = createMode;
- this.viewState = new ViewState(dockAreaAlpha);
+ this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
+ R.string.recents_drag_hint_message);
this.dockArea = dockArea;
this.touchArea = touchArea;
this.expandedTouchDockArea = expandedTouchDockArea;
}
/**
+ * Updates the dock state with the given context.
+ */
+ public void update(Context context) {
+ viewState.update(context);
+ }
+
+ /**
* Returns whether {@param x} and {@param y} are contained in the area scaled to the
* given {@param width} and {@param height}.
*/
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;
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;
private ImageView mBadgeView;
private Task mTask;
private boolean mDismissState;
+ private int mCornerRadius;
private ViewFocusAnimator mViewFocusAnimator;
private DismissAnimationsHolder mDismissAnimationsHolder;
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);
}
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) {
// Calculate the offscreen task rect (for tasks that are not backed by views)
float stackScroll = stackView.getScroller().getStackScroll();
TaskView taskView = stackView.getChildViewForTask(task);
- TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm();
- Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect);
- offscreenTaskRect.offsetTo(offscreenTaskRect.left,
- layoutAlgorithm.mStackRect.bottom);
+ TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
+ Rect offscreenTaskRect = new Rect();
+ stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
// If this is a full screen stack, the transition will be towards the single, full screen
// task. We only need the transition spec for this task.
if (taskView == null) {
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else {
- layoutAlgorithm.getStackTransformScreenCoordinates(task, stackScroll, mTmpTransform,
- null);
+ mTmpTransform.fillIn(taskView);
+ stackLayout.transformToScreenCoordinates(mTmpTransform);
specs.add(composeAnimationSpec(stackView, taskView, mTmpTransform,
true /* addHeaderBitmap */));
}
// never happen)
specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
} else {
- layoutAlgorithm.getStackTransformScreenCoordinates(t, stackScroll,
- mTmpTransform, null);
+ mTmpTransform.fillIn(taskView);
+ stackLayout.transformToScreenCoordinates(mTmpTransform);
specs.add(composeAnimationSpec(stackView, tv, mTmpTransform,
true /* addHeaderBitmap */));
}
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
- private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 150;
+ private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
private TaskStack mStack;
R.dimen.recents_task_view_rounded_corners_radius);
mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
this, false);
- mStackActionButton.forceHasOverlappingRendering(false);
mStackActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* Called from RecentsActivity when the task stack is updated.
*/
- public void updateStack(TaskStack stack) {
+ public void updateStack(TaskStack stack, boolean setStackViewTasks) {
mStack = stack;
- mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
+ if (setStackViewTasks) {
+ mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
+ }
// Update the top level view's visibilities
if (stack.getTaskCount() > 0) {
ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
for (int i = visDockStates.size() - 1; i >= 0; i--) {
- Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
- if (d.getAlpha() > 0) {
- d.draw(canvas);
- }
+ visDockStates.get(i).viewState.draw(canvas);
}
}
public final void onBusEvent(DragStartEvent event) {
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+ TaskStack.DockState.NONE.viewState.hintTextAlpha,
true /* animateAlpha */, false /* animateBounds */);
+
+ // Temporarily hide the stack action button without changing visibility
+ if (mStackActionButton != null) {
+ mStackActionButton.animate()
+ .alpha(0f)
+ .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .start();
+ }
}
public final void onBusEvent(DragDropTargetChangedEvent event) {
if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+ TaskStack.DockState.NONE.viewState.hintTextAlpha,
true /* animateAlpha */, true /* animateBounds */);
} else {
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
updateVisibleDockRegions(new TaskStack.DockState[] {dockState},
- false /* isDefaultDockState */, -1, true /* animateAlpha */,
+ false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
true /* animateBounds */);
}
if (mStackActionButton != null) {
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
// Hide the dock region
- updateVisibleDockRegions(null, false /* isDefaultDockState */, -1,
+ updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
false /* animateAlpha */, false /* animateBounds */);
- TaskStackLayoutAlgorithm stackLayout = mTaskStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mTaskStackView.getScroller();
- TaskViewTransform tmpTransform = new TaskViewTransform();
-
// We translated the view but we need to animate it back from the current layout-space
// rect to its final layout-space rect
int x = (int) event.taskView.getTranslationX();
event.task.getTopComponent().flattenToShortString());
} else {
// Animate the overlay alpha back to 0
- updateVisibleDockRegions(null, true /* isDefaultDockState */, -1,
+ updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
true /* animateAlpha */, false /* animateBounds */);
}
+
+ // Show the stack action button again without changing visibility
+ if (mStackActionButton != null) {
+ mStackActionButton.animate()
+ .alpha(1f)
+ .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .start();
+ }
}
private Rect getTaskRect(TaskView taskView) {
hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
}
+ public final void onBusEvent(MultiWindowStateChangedEvent event) {
+ updateStack(event.stack, false /* setStackViewTasks */);
+ }
+
/**
* Shows the stack action button.
*/
* Updates the dock region to match the specified dock state.
*/
private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
- boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha,
- boolean animateBounds) {
+ boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
+ boolean animateAlpha, boolean animateBounds) {
ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
new ArraySet<TaskStack.DockState>());
ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
TaskStack.DockState.ViewState viewState = dockState.viewState;
if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
// This is no longer visible, so hide it
- viewState.startAnimation(null, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
+ viewState.startAnimation(null, 0, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
Interpolators.ALPHA_OUT, animateAlpha, animateBounds);
} else {
// This state is now visible, update the bounds and show it
- int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha);
+ int areaAlpha = overrideAreaAlpha != -1
+ ? overrideAreaAlpha
+ : viewState.dockAreaAlpha;
+ int hintAlpha = overrideHintAlpha != -1
+ ? overrideHintAlpha
+ : viewState.hintTextAlpha;
Rect bounds = isDefaultDockState
? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight())
: dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
viewState.dockAreaOverlay.setCallback(this);
viewState.dockAreaOverlay.setBounds(bounds);
}
- viewState.startAnimation(bounds, alpha, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
- Interpolators.ALPHA_IN, animateAlpha, animateBounds);
+ viewState.startAnimation(bounds, areaAlpha, hintAlpha,
+ DOCK_AREA_OVERLAY_TRANSITION_DURATION, Interpolators.ALPHA_IN,
+ animateAlpha, animateBounds);
}
}
}
TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
for (TaskStack.DockState dockState : dockStates) {
registerDropTargetForCurrentDrag(dockState);
+ dockState.update(mRv.getContext());
mVisibleDockStates.add(dockState);
}
}
}
public final void onBusEvent(ConfigurationChangedEvent event) {
- updateSnapAlgorithm();
+ if (event.fromDisplayDensityChange) {
+ updateSnapAlgorithm();
+ }
}
/**
private boolean mHasNavBarScrim;
private boolean mShouldAnimateNavBarScrim;
-
+ private boolean mHasTransposedNavBar;
+ private boolean mHasDockedTasks;
private int mNavBarScrimEnterDuration;
public SystemBarScrimViews(RecentsActivity activity) {
mNavBarScrimView.forceHasOverlappingRendering(false);
mNavBarScrimEnterDuration = activity.getResources().getInteger(
R.integer.recents_nav_bar_scrim_enter_duration);
+ mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavBar();
+ mHasDockedTasks = Recents.getSystemServices().hasDockedTask();
}
/**
* @return Whether to show the nav bar scrim.
*/
private boolean isNavBarScrimRequired(boolean hasStackTasks) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- return hasStackTasks && !ssp.hasTransposedNavBar() && !ssp.hasDockedTask();
+ return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
}
/**** EventBus events ****/
}
public final void onBusEvent(ConfigurationChangedEvent event) {
+ if (event.fromDeviceOrientationChange) {
+ mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavBar();
+ }
animateScrimToCurrentNavBarState(event.hasStackTasks);
}
public final void onBusEvent(MultiWindowStateChangedEvent event) {
- animateScrimToCurrentNavBarState(event.hasStackTasks);
+ mHasDockedTasks = event.inMultiWindow;
+ animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
}
/**
// Get the final set of task transforms
mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
- mTmpFinalTaskTransforms);
+ true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
// Focus the task view
TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
int duration;
Interpolator interpolator;
if (willScrollToFront) {
- duration = Math.max(100, 100 + ((i - 1) * 50));
+ duration = calculateStaggeredAnimDuration(i);
interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
} else {
if (i < newFocusTaskViewIndex) {
}
return willScroll;
}
+
+ /**
+ * Starts the animation to go to the initial stack layout with a task focused. In addition, the
+ * previous task will be animated in after the scroll completes.
+ */
+ public void startNewStackScrollAnimation(TaskStack newStack,
+ ReferenceCountedTrigger animationTrigger) {
+ TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+ TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+ // Get the current set of task transforms
+ ArrayList<Task> stackTasks = newStack.getStackTasks();
+ mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
+
+ // Update the stack
+ mStackView.setTasks(newStack, false /* allowNotifyStackChanges */);
+ mStackView.updateLayoutAlgorithm(false /* boundScroll */);
+
+ // Pick up the newly visible views after the scroll
+ final float newScroll = stackLayout.mInitialScrollP;
+ mStackView.bindVisibleTaskViews(newScroll);
+
+ // Update the internal state
+ stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+ stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */);
+ stackScroller.setStackScroll(newScroll);
+ mStackView.cancelDeferredTaskViewLayoutAnimation();
+
+ // Get the final set of task transforms
+ mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
+ false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
+
+ // Hide the front most task view until the scroll is complete
+ Task frontMostTask = newStack.getStackFrontMostTask(false /* includeFreeform */);
+ final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
+ final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
+ stackTasks.indexOf(frontMostTask));
+ if (frontMostTaskView != null) {
+ mStackView.updateTaskViewToTransform(frontMostTaskView,
+ stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE);
+ }
+
+ // Setup the end listener to return all the hidden views to the view pool after the
+ // focus animation
+ animationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mStackView.bindVisibleTaskViews(newScroll);
+
+ // Now, animate in the front-most task
+ if (frontMostTaskView != null) {
+ mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform,
+ new AnimationProps(75, 200, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR));
+ }
+ }
+ });
+
+ List<TaskView> taskViews = mStackView.getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+
+ if (mStackView.isIgnoredTask(task)) {
+ continue;
+ }
+ if (task == frontMostTask && frontMostTaskView != null) {
+ continue;
+ }
+
+ int taskIndex = stackTasks.indexOf(task);
+ TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
+ TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
+
+ // Update the task to the initial state (for the newly picked up tasks)
+ mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
+
+ int duration = calculateStaggeredAnimDuration(i);
+ Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+
+ AnimationProps anim = new AnimationProps()
+ .setDuration(AnimationProps.BOUNDS, duration)
+ .setInterpolator(AnimationProps.BOUNDS, interpolator)
+ .setListener(animationTrigger.decrementOnAnimationEnd());
+ animationTrigger.increment();
+ mStackView.updateTaskViewToTransform(tv, toTransform, anim);
+ }
+ }
+
+ /**
+ * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and
+ * {@link #startNewStackScrollAnimation}.
+ */
+ private int calculateStaggeredAnimDuration(int i) {
+ return Math.max(100, 100 + ((i - 1) * 50));
+ }
}
mCb = cb;
mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
- mBaseTopMargin = getDimensionForDevice(res,
+ mBaseTopMargin = getDimensionForDevice(context,
R.dimen.recents_layout_top_margin_phone,
R.dimen.recents_layout_top_margin_tablet,
R.dimen.recents_layout_top_margin_tablet_xlarge);
- mBaseSideMargin = getDimensionForDevice(res,
+ mBaseSideMargin = getDimensionForDevice(context,
R.dimen.recents_layout_side_margin_phone,
R.dimen.recents_layout_side_margin_tablet,
R.dimen.recents_layout_side_margin_tablet_xlarge);
res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
- mBaseInitialTopOffset = getDimensionForDevice(res,
+ mBaseInitialTopOffset = getDimensionForDevice(context,
R.dimen.recents_layout_initial_top_offset_phone_port,
R.dimen.recents_layout_initial_top_offset_phone_land,
R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet,
R.dimen.recents_layout_initial_top_offset_tablet);
- mBaseInitialBottomOffset = getDimensionForDevice(res,
+ mBaseInitialBottomOffset = getDimensionForDevice(context,
R.dimen.recents_layout_initial_bottom_offset_phone_port,
R.dimen.recents_layout_initial_bottom_offset_phone_land,
R.dimen.recents_layout_initial_bottom_offset_tablet,
mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
- // Anchor the task rect top aligned to the non-freeform stack rect
- float aspect = (float) (windowRect.width() - (mSystemInsets.left + mSystemInsets.right)) /
- (windowRect.height() - (mSystemInsets.top + mSystemInsets.bottom));
- int minHeight = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
- int height = (int) Math.min(mStackRect.width() / aspect, minHeight);
+ // Anchor the task rect top aligned to the stack rect
+ int height = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
// Short circuit here if the stack rects haven't changed so we don't do all the work below
/**
* Creates task overrides to ensure the initial stack layout if necessary.
*/
- public void setTaskOverridesForInitialState(TaskStack stack) {
+ public void setTaskOverridesForInitialState(TaskStack stack, boolean ignoreScrollToFront) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
mTaskIndexOverrideMap.clear();
boolean scrollToFront = launchState.launchedFromHome ||
launchState.launchedViaDockGesture;
if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
- if (!launchState.launchedWithAltTab && !scrollToFront) {
+ if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
// Set the initial scroll to the predefined state (which differs from the stack)
float [] initialNormX = new float[] {
getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
*/
public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
- Rect windowRect = Recents.getSystemServices().getWindowRect();
TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
transformOut, frontTransform, true /* forceUpdate */,
false /* ignoreTaskOverrides */);
- transform.rect.offset(windowRect.left, windowRect.top);
- return transform;
+ return transformToScreenCoordinates(transform);
+ }
+
+ /**
+ * Transforms the given {@param transformOut} to the screen coordinates.
+ */
+ public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut) {
+ Rect windowRect = Recents.getSystemServices().getWindowRect();
+ transformOut.rect.offset(windowRect.left, windowRect.top);
+ return transformOut;
}
/**
* stack.
*/
float getStackScrollForTask(Task t) {
- return mTaskIndexOverrideMap.get(t.key.id, (float) mTaskIndexMap.get(t.key.id, 0));
+ Float overrideP = mTaskIndexOverrideMap.get(t.key.id, null);
+ if (overrideP == null) {
+ return (float) mTaskIndexMap.get(t.key.id, 0);
+ }
+ return overrideP;
}
/**
int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
WIDTH);
int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
- if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+ if (Utilities.getAppConfiguration(mContext).orientation
+ == Configuration.ORIENTATION_LANDSCAPE) {
// If we are in landscape, calculate the width of the stack in portrait and ensure that
// we are not larger than that size
Rect portraitDisplayRect = new Rect(0, 0,
/**
* Retrieves resources that are constant regardless of the current configuration of the device.
*/
- public static int getDimensionForDevice(Resources res, int phoneResId,
+ public static int getDimensionForDevice(Context ctx, int phoneResId,
int tabletResId, int xlargeTabletResId) {
- return getDimensionForDevice(res, phoneResId, phoneResId, tabletResId, tabletResId,
+ return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
xlargeTabletResId, xlargeTabletResId);
}
/**
* Retrieves resources that are constant regardless of the current configuration of the device.
*/
- public static int getDimensionForDevice(Resources res, int phonePortResId, int phoneLandResId,
+ public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
int xlargeTabletLandResId) {
RecentsConfiguration config = Recents.getConfiguration();
- boolean isLandscape = Recents.getSystemServices().getDisplayOrientation() ==
+ Resources res = ctx.getResources();
+ boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
Configuration.ORIENTATION_LANDSCAPE;
if (config.isXLargeScreen) {
return res.getDimensionPixelSize(isLandscape
*/
public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
boolean isInitialized = mLayoutAlgorithm.isInitialized();
+
// Only notify if we are already initialized, otherwise, everything will pick up all the
// new and old tasks when we next layout
mStack.setTasks(getContext(), stack.computeAllTasksList(),
*/
public void updateToInitialState() {
mStackScroller.setStackScrollToInitialState();
- mLayoutAlgorithm.setTaskOverridesForInitialState(mStack);
+ mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
}
/** Updates the list of task views */
* Binds the visible {@link TaskView}s at the given target scroll.
*/
void bindVisibleTaskViews(float targetStackScroll) {
- bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */);
- }
-
- void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
- bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides);
+ bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
}
/**
* @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
* includes those visible at the current stack scroll, and all at the
* target stack scroll.
- * @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible
- * {@link TaskView}s
* @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
* tasks at their non-overridden task progress
*/
- void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet,
- boolean ignoreTaskOverrides) {
+ void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
// Get all the task transforms
ArrayList<Task> tasks = mStack.getStackTasks();
int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
- mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet,
+ mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
ignoreTaskOverrides);
// Return all the invisible children to the pool
Task task = tv.getTask();
// Skip ignored tasks
- if (ignoreTasksSet.contains(task.key)) {
+ if (mIgnoreTasks.contains(task.key)) {
continue;
}
TaskViewTransform transform = mCurrentTaskTransforms.get(i);
// Skip ignored tasks
- if (ignoreTasksSet.contains(task.key)) {
+ if (mIgnoreTasks.contains(task.key)) {
continue;
}
}
/**
- * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
+ * @see #relayoutTaskViews(AnimationProps, boolean)
*/
public void relayoutTaskViews(AnimationProps animation) {
- relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */);
+ relayoutTaskViews(animation, false /* ignoreTaskOverrides */);
}
/**
* {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
* animations that are current running on those task views, and will ensure that the children
* {@link TaskView}s will match the set of visible tasks in the stack.
- *
- * @param ignoreTasksSet the set of tasks to ignore in the relayout
*/
- private void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
- boolean ignoreTaskOverrides) {
+ private void relayoutTaskViews(AnimationProps animation, boolean ignoreTaskOverrides) {
// If we had a deferred animation, cancel that
mDeferredTaskViewLayoutAnimation = null;
// Synchronize the current set of TaskViews
- bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
+ bindVisibleTaskViews(mStackScroller.getStackScroll(),
ignoreTaskOverrides /* ignoreTaskOverrides */);
// Animate them to their final transforms with the given animation
int taskIndex = mStack.indexOfStackTask(tv.getTask());
TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
- if (ignoreTasksSet.contains(tv.getTask().key)) {
+ if (mIgnoreTasks.contains(tv.getTask().key)) {
continue;
}
* {@param stackScroll} and {@param focusState}.
*/
public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
- ArrayList<TaskViewTransform> transformsOut) {
+ boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
Utilities.matchTaskListSize(tasks, transformsOut);
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i);
mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
- true /* forceUpdate */, true /* ignoreTaskOverrides */);
+ true /* forceUpdate */, ignoreTaskOverrides);
transform.visible = true;
}
}
/**
* Updates the layout algorithm min and max virtual scroll bounds.
- *
- * @see #updateLayoutAlgorithm(boolean, ArraySet<Task.TaskKey>)
*/
public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
- updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks);
- }
-
- /**
- * Updates the min and max virtual scroll bounds.
- *
- * @param ignoreTasksSet the set of tasks to ignore in the relayout
- */
- private void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
- ArraySet<Task.TaskKey> ignoreTasksSet) {
// Compute the min and max scroll values
- mLayoutAlgorithm.update(mStack, ignoreTasksSet);
+ mLayoutAlgorithm.update(mStack, mIgnoreTasks);
// Update the freeform workspace background
SystemServicesProxy ssp = Recents.getSystemServices();
}
// Rebind all the views, including the ignore ones
- bindVisibleTaskViews(mStackScroller.getStackScroll(), mIgnoreTasks,
- false /* ignoreTaskOverrides */);
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
// Measure each of the TaskViews
mTmpTaskViews.clear();
tv.onTaskBound(task);
// Load the task data
- Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
+ Recents.getTaskLoader().loadTaskData(task);
}
private void unbindTaskView(TaskView tv, Task task) {
}
if (launchTaskIndex != -1) {
// Stop all animations
- mUIDozeTrigger.stopDozing();
cancelAllTaskViewAnimations();
final Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
updateLayoutAlgorithm(true /* boundScroll */);
addIgnoreTask(event.task);
}
- relayoutTaskViews(animation, mIgnoreTasks, ignoreTaskOverrides);
+ relayoutTaskViews(animation, ignoreTaskOverrides);
}
public final void onBusEvent(final DragEndEvent event) {
}
}
- public final void onBusEvent(MultiWindowStateChangedEvent event) {
- if (!event.inMultiWindow) {
+ public final void onBusEvent(final MultiWindowStateChangedEvent event) {
+ if (event.inMultiWindow) {
+ setTasks(event.stack, true /* allowNotifyStackChanges */);
+ } else {
+ // Reset the launch state before handling the multiwindow change
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ launchState.reset();
+
// Defer until the next frame to ensure that we have received all the system insets, and
// initial layout updates
+ event.getAnimationTrigger().increment();
post(new Runnable() {
@Override
public void run() {
// Scroll the stack to the front to see the undocked task
- mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
- @Override
- public void run() {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- tv.getHeaderView().rebindToTask(tv.getTask(),
- tv.mTouchExplorationEnabled, tv.mIsDisabledInSafeMode);
- }
- }
- });
+ mAnimationHelper.startNewStackScrollAnimation(event.stack,
+ event.getAnimationTrigger());
+ event.getAnimationTrigger().decrement();
}
});
}
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
private static final int INACTIVE_POINTER_ID = -1;
- private static final Interpolator STACK_TRANSFORM_INTERPOLATOR =
- new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f);
+ private static final float CHALLENGING_SWIPE_ESCAPE_VELOCITY = 800f; // dp/sec
// The min overscroll is the amount of task progress overscroll we want / the max overscroll
// curve value below
private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
@Override
protected float getSize(View v) {
- return mSv.getWidth();
+ return getScaledDismissSize();
}
@Override
anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mSwipeHelperAnimations.put(v, anim);
}
+
+ @Override
+ protected float getUnscaledEscapeVelocity() {
+ return CHALLENGING_SWIPE_ESCAPE_VELOCITY;
+ }
+
+ @Override
+ protected long getMaxEscapeAnimDuration() {
+ return 700;
+ }
};
mSwipeHelper.setDisableHardwareLayers(true);
}
// Get the final set of task transforms (with task removed)
mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
- mCurrentTasks, mFinalTaskTransforms);
+ mCurrentTasks, true /* ignoreTaskOverrides */, mFinalTaskTransforms);
// Set the target to scroll towards upon dismissal
mTargetStackScroll = newStackScroll;
@Override
public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
- updateTaskViewTransforms(getDismissFraction(v));
+ updateTaskViewTransforms(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(swipeProgress));
return true;
}
}
/**
- * Returns the fraction which we should interpolate the other task views based on the dismissal
- * of this given task.
- *
- * TODO: We can interpolate this to adjust when the other tasks should respond to the dismissal
+ * Returns the scaled size used to calculate the dismiss fraction.
*/
- private float getDismissFraction(View v) {
- float fraction = Math.min(1f, Math.abs(v.getTranslationX() / mSv.getWidth()));
- return STACK_TRANSFORM_INTERPOLATOR.getInterpolation(fraction);
+ private float getScaledDismissSize() {
+ return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight());
}
}
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Outline;
-import android.graphics.Paint;
import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatProperty;
mTask = t;
mTask.addCallback(this);
mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
+ mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode);
+ mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
if (!t.isDockable && ssp.hasDockedTask()) {
if (mIncompatibleAppToastView == null) {
@Override
public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
- // Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode);
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+ // Update each of the views to the new task data
+ mThumbnailView.onTaskDataLoaded(thumbnailInfo);
+ mHeaderView.onTaskDataLoaded();
mTaskDataLoaded = true;
}
@Override
public void onTaskDataUnloaded() {
- // Unbind each of the views from the task data and remove the task callback
+ // Unbind each of the views from the task and remove the task callback
mTask.removeCallback(this);
mThumbnailView.unbindFromTask();
mHeaderView.unbindFromTask(mTouchExplorationEnabled);
@Override
public void onTaskStackIdChanged() {
- mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+ // Force rebind the header, the thumbnail does not change due to stack changes
+ mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
+ mHeaderView.onTaskDataLoaded();
}
/**** View.OnClickListener Implementation ****/
// Update the dimensions of everything in the header. We do this because we need to use
// resources for the display, and not the current configuration.
Resources res = getResources();
- int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land,
R.dimen.recents_task_view_header_height,
R.dimen.recents_task_view_header_height_tablet_land);
- int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding,
R.dimen.recents_task_view_header_button_padding,
}
}
- /** Binds the bar view to the task */
- public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
+ /**
+ * Binds the bar view to the task.
+ */
+ public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
mTask = t;
- // If an activity icon is defined, then we use that as the primary icon to show in the bar,
- // otherwise, we fall back to the application icon
int primaryColor = disabledInSafeMode
? mDisabledTaskBarBackgroundColor
: t.colorPrimary;
if (mBackground.getColor() != primaryColor) {
updateBackgroundColor(primaryColor, mDimAlpha);
}
- if (t.icon != null) {
- mIconView.setImageDrawable(t.icon);
- }
if (!mTitleView.getText().toString().equals(t.title)) {
mTitleView.setText(t.title);
}
}
}
+ /**
+ * Called when the bound task's data has loaded and this view should update to reflect the
+ * changes.
+ */
+ public void onTaskDataLoaded() {
+ if (mTask.icon != null) {
+ mIconView.setImageDrawable(mTask.icon);
+ }
+ }
+
/** Unbinds the bar view from the task */
void unbindFromTask(boolean touchExplorationEnabled) {
mTask = null;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
SystemServicesProxy ssp = Recents.getSystemServices();
- mOrientation = ssp.getDisplayOrientation();
+ mOrientation = Utilities.getAppConfiguration(mContext).orientation;
mDisplayRect = ssp.getDisplayRect();
}
updateThumbnailPaintFilter();
}
- /** Binds the thumbnail view to the task */
- void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo,
- boolean disabledInSafeMode) {
+ /**
+ * Binds the thumbnail view to the task.
+ */
+ void bindToTask(Task t, boolean disabledInSafeMode) {
mTask = t;
mDisabledInSafeMode = disabledInSafeMode;
- if (t.thumbnail != null) {
- setThumbnail(t.thumbnail, thumbnailInfo);
- } else {
- setThumbnail(null, null);
- }
if (t.colorBackground != 0) {
mBgFillPaint.setColor(t.colorBackground);
}
}
+ /**
+ * Called when the bound task's data has loaded and this view should update to reflect the
+ * changes.
+ */
+ void onTaskDataLoaded(ActivityManager.TaskThumbnailInfo thumbnailInfo) {
+ if (mTask.thumbnail != null) {
+ setThumbnail(mTask.thumbnail, thumbnailInfo);
+ } else {
+ setThumbnail(null, null);
+ }
+ }
+
/** Unbinds the thumbnail view from the task */
void unbindFromTask() {
mTask = null;
final float fontScale = newConfig.fontScale;
final int density = newConfig.densityDpi;
if (density != mDensity || mFontScale != fontScale) {
- reInflateViews();
+ onDensityOrFontScaleChanged();
mDensity = density;
mFontScale = fontScale;
}
}
}
- protected void reInflateViews() {
+ protected void onDensityOrFontScaleChanged() {
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
for (int i = 0; i < activeNotifications.size(); i++) {
Entry entry = activeNotifications.get(i);
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;
private int mMaxExpandHeight;
private int mHeadsUpHeight;
private View mVetoButton;
+ private int mNotificationColor;
private boolean mClearable;
private ExpansionLogger mLogger;
private String mLoggingKey;
mPrivateLayout.onNotificationUpdated(entry);
mPublicLayout.onNotificationUpdated(entry);
mShowingPublicInitialized = false;
+ updateNotificationColor();
updateClearability();
if (mIsSummaryWithChildren) {
recreateNotificationHeader();
}
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() {
import android.app.Notification;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
public class StatusBarIconView extends AnimatedImageView {
private static final String TAG = "StatusBarIconView";
+ private boolean mAlwaysScaleIcon;
private StatusBarIcon mIcon;
@ViewDebug.ExportedProperty private String mSlot;
private String mNumberText;
private Notification mNotification;
private final boolean mBlocked;
+ private int mDensity;
public StatusBarIconView(Context context, String slot, Notification notification) {
this(context, slot, notification, false);
public StatusBarIconView(Context context, String slot, Notification notification,
boolean blocked) {
super(context);
- final Resources res = context.getResources();
mBlocked = blocked;
mSlot = slot;
mNumberPain = new Paint();
mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
mNumberPain.setAntiAlias(true);
setNotification(notification);
+ maybeUpdateIconScale();
+ setScaleType(ScaleType.CENTER);
+ mDensity = context.getResources().getDisplayMetrics().densityDpi;
+ }
+ private void maybeUpdateIconScale() {
// We do not resize and scale system icons (on the right), only notification icons (on the
// left).
- if (notification != null) {
- final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
- final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
- final float scale = (float)imageBounds / (float)outerBounds;
- setScaleX(scale);
- setScaleY(scale);
+ if (mNotification != null || mAlwaysScaleIcon) {
+ updateIconScale();
}
+ }
- setScaleType(ScaleType.CENTER);
+ private void updateIconScale() {
+ Resources res = mContext.getResources();
+ final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
+ final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
+ final float scale = (float)imageBounds / (float)outerBounds;
+ setScaleX(scale);
+ setScaleY(scale);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ int density = newConfig.densityDpi;
+ if (density != mDensity) {
+ mDensity = density;
+ maybeUpdateIconScale();
+ updateDrawable();
+ }
}
public void setNotification(Notification notification) {
public StatusBarIconView(Context context, AttributeSet attrs) {
super(context, attrs);
mBlocked = false;
- final Resources res = context.getResources();
- final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
- final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
- final float scale = (float)imageBounds / (float)outerBounds;
- setScaleX(scale);
- setScaleY(scale);
+ mAlwaysScaleIcon = true;
+ updateIconScale();
+ mDensity = context.getResources().getDisplayMetrics().densityDpi;
}
private static boolean streq(String a, String b) {
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.View;
private static final String TAG = "IconMerger";
private static final boolean DEBUG = false;
- private final int mIconSize;
- private final int mIconHPadding;
+ private int mIconSize;
+ private int mIconHPadding;
private View mMoreView;
public IconMerger(Context context, AttributeSet attrs) {
super(context, attrs);
+ reloadDimens();
+ if (DEBUG) {
+ setBackgroundColor(0x800099FF);
+ }
+ }
- Resources res = context.getResources();
+ private void reloadDimens() {
+ Resources res = mContext.getResources();
mIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+ }
- if (DEBUG) {
- setBackgroundColor(0x800099FF);
- }
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ reloadDimens();
}
public void setOverflowIndicator(View v) {
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
+import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import java.util.ArrayList;
-import java.util.List;
/**
* A controller for the space in the status bar to the left of the system icons. This area is
* Initializes the views that will represent the notification area.
*/
protected void initializeNotificationAreaViews(Context context) {
- Resources res = context.getResources();
- mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
- mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+ reloadDimens(context);
LayoutInflater layoutInflater = LayoutInflater.from(context);
mNotificationIconArea = inflateIconArea(layoutInflater);
}
}
+ public void onDensityOrFontScaleChanged(Context context) {
+ reloadDimens(context);
+ final LinearLayout.LayoutParams params = generateIconLayoutParams();
+ for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
+ View child = mNotificationIcons.getChildAt(i);
+ child.setLayoutParams(params);
+ }
+ }
+
+ @NonNull
+ private LinearLayout.LayoutParams generateIconLayoutParams() {
+ return new LinearLayout.LayoutParams(
+ mIconSize + 2 * mIconHPadding, getHeight());
+ }
+
+ private void reloadDimens(Context context) {
+ Resources res = context.getResources();
+ mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+ mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+ }
+
/**
* Returns the view that represents the notification area.
*/
* Updates the notifications with the given list of notifications to display.
*/
public void updateNotificationIcons(NotificationData notificationData) {
- final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- mIconSize + 2 * mIconHPadding, getHeight());
+ final LinearLayout.LayoutParams params = generateIconLayoutParams();
ArrayList<NotificationData.Entry> activeNotifications =
notificationData.getActiveNotifications();
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
mStackScroller.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setOnGroupChangeListener(mStackScroller);
- mKeyguardIconOverflowContainer =
- (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
- mKeyguardIconOverflowContainer.setOnActivatedListener(this);
- mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
- mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
-
-
+ inflateOverflowContainer();
inflateEmptyShadeView();
inflateDismissView();
mExpandedContents = mStackScroller;
return new BatteryControllerImpl(mContext);
}
+ private void inflateOverflowContainer() {
+ mKeyguardIconOverflowContainer =
+ (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
+ mKeyguardIconOverflowContainer.setOnActivatedListener(this);
+ mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
+ mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
+ }
+
@Override
- protected void reInflateViews() {
- super.reInflateViews();
+ protected void onDensityOrFontScaleChanged() {
+ super.onDensityOrFontScaleChanged();
+ mScrimController.onDensityOrFontScaleChanged();
+ mStatusBarView.onDensityOrFontScaleChanged();
+ mBrightnessMirrorController.onDensityOrFontScaleChanged();
+ inflateSignalClusters();
+ mIconController.onDensityOrFontScaleChanged();
inflateDismissView();
updateClearAll();
inflateEmptyShadeView();
updateEmptyShadeView();
+ inflateOverflowContainer();
+ }
+
+ private void inflateSignalClusters() {
+ SignalClusterView signalClusterView = reinflateSignalCluster(mStatusBarView);
+ mIconController.setSignalCluster(signalClusterView);
+ reinflateSignalCluster(mKeyguardStatusView);
+ }
+
+ private SignalClusterView reinflateSignalCluster(View view) {
+ SignalClusterView signalCluster =
+ (SignalClusterView) view.findViewById(R.id.signal_cluster);
+ if (signalCluster != null) {
+ ViewParent parent = signalCluster.getParent();
+ if (parent instanceof ViewGroup) {
+ ViewGroup viewParent = (ViewGroup) parent;
+ int index = viewParent.indexOfChild(signalCluster);
+ viewParent.removeView(signalCluster);
+ SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(mContext)
+ .inflate(R.layout.signal_cluster_view, viewParent, false);
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams();
+ layoutParams.setMarginsRelative(
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.signal_cluster_margin_start),
+ 0, 0, 0);
+ newCluster.setLayoutParams(layoutParams);
+ newCluster.setSecurityController(mSecurityController);
+ newCluster.setNetworkController(mNetworkController);
+ viewParent.addView(newCluster, index);
+ return newCluster;
+ }
+ return signalCluster;
+ }
+ return null;
}
private void inflateEmptyShadeView() {
// SystemUIService notifies SystemBars of configuration changes, which then calls down here
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ updateResources();
+ updateDisplaySize(); // populates mDisplayMetrics
super.onConfigurationChanged(newConfig); // calls refreshLayout
if (DEBUG) {
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
- updateDisplaySize(); // populates mDisplayMetrics
- updateResources();
repositionNavigationBar();
updateRowStates();
- mIconController.updateResources();
+ mIconController.defineSlots();
mScreenPinningRequest.onConfigurationChanged();
mNetworkController.onConfigurationChanged();
}
mMaxAllowedKeyguardNotifications = res.getInteger(
R.integer.keyguard_max_notification_count);
- if (DEBUG) Log.v(TAG, "updateResources");
+ if (DEBUG) Log.v(TAG, "defineSlots");
}
// Visibility reporting
import android.util.EventLog;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogTags;
+import com.android.systemui.R;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
float scrimFraction = Math.max(mPanelFraction, mMinFraction);
mScrimController.setPanelExpansion(scrimFraction);
}
+
+ public void onDensityOrFontScaleChanged() {
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ layoutParams.height = getResources().getDimensionPixelSize(
+ R.dimen.status_bar_height);
+ setLayoutParams(layoutParams);
+ }
}
import android.graphics.Color;
import android.graphics.Rect;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
mScrimBehind.setChangeRunnable(changeRunnable);
}
+
+ public void onDensityOrFontScaleChanged() {
+ ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
+ layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
+ R.dimen.heads_up_scrim_height);
+ mHeadsUpScrim.setLayoutParams(layoutParams);
+ }
}
private View mNotificationIconAreaInner;
private BatteryMeterView mBatteryMeterView;
+ private BatteryMeterView mBatteryMeterViewKeyguard;
private TextView mClock;
private int mIconSize;
mStatusIconsKeyguard = (LinearLayout) keyguardStatusBar.findViewById(R.id.statusIcons);
mBatteryMeterView = (BatteryMeterView) statusBar.findViewById(R.id.battery);
- maybeScaleBatteryMeterView(context);
+ mBatteryMeterViewKeyguard = (BatteryMeterView) keyguardStatusBar.findViewById(R.id.battery);
+ scaleBatteryMeterViews(context);
mClock = (TextView) statusBar.findViewById(R.id.clock);
mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
mHandler = new Handler();
- updateResources();
+ defineSlots();
+ loadDimens();
TunerService.get(mContext).addTunable(this, ICON_BLACKLIST);
}
+ public void setSignalCluster(SignalClusterView signalCluster) {
+ mSignalCluster = signalCluster;
+ }
+
/**
- * Looks up the scale factor for status bar icons and scales the battery view by that amount
- * if appropriate.
+ * Looks up the scale factor for status bar icons and scales the battery view by that amount.
*/
- private void maybeScaleBatteryMeterView(Context context) {
+ private void scaleBatteryMeterViews(Context context) {
Resources res = context.getResources();
TypedValue typedValue = new TypedValue();
res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
float iconScaleFactor = typedValue.getFloat();
- if (iconScaleFactor == 1.f) {
- return;
- }
-
- float batteryHeight = res.getDimension(R.dimen.status_bar_battery_icon_height);
- float batteryWidth = res.getDimension(R.dimen.status_bar_battery_icon_width);
+ int batteryHeight = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_height);
+ int batteryWidth = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_width);
+ int marginBottom = res.getDimensionPixelSize(R.dimen.battery_margin_bottom);
LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams(
(int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor));
+ scaledLayoutParams.setMarginsRelative(0, 0, 0, marginBottom);
mBatteryMeterView.setLayoutParams(scaledLayoutParams);
+ mBatteryMeterViewKeyguard.setLayoutParams(scaledLayoutParams);
}
@Override
setIcon(views.get(i).getSlot(), views.get(i).getStatusBarIcon());
}
}
-
- public void updateResources() {
+ private void loadDimens() {
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
+ }
+
+ public void defineSlots() {
defineSlots(mContext.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
- FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size);
}
private void addSystemIcon(int index, StatusBarIcon icon) {
}
return ret;
}
+
+ public void onDensityOrFontScaleChanged() {
+ loadDimens();
+ mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+ updateClock();
+ for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
+ View child = mStatusIcons.getChildAt(i);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
+ child.setLayoutParams(lp);
+ }
+ for (int i = 0; i < mStatusIconsKeyguard.getChildCount(); i++) {
+ View child = mStatusIconsKeyguard.getChildAt(i);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ child.setLayoutParams(lp);
+ }
+ scaleBatteryMeterViews(mContext);
+ }
+
+ private void updateClock() {
+ FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size);
+ mClock.setPaddingRelative(
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_clock_starting_padding),
+ 0,
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_clock_end_padding),
+ 0);
+ }
}
private ArrayList<StatusBarIcon> mIcons = new ArrayList<>();
public void defineSlots(String[] slots) {
+ mSlots.clear();
final int N = slots.length;
for (int i=0; i < N; i++) {
mSlots.add(slots[i]);
- mIcons.add(null);
+ if (mIcons.size() < mSlots.size()) {
+ mIcons.add(null);
+ }
}
}
package com.android.systemui.statusbar.policy;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
public long TRANSITION_DURATION_OUT = 150;
public long TRANSITION_DURATION_IN = 200;
+ private final StatusBarWindowView mStatusBarWindow;
private final ScrimView mScrimBehind;
- private final View mBrightnessMirror;
private final View mNotificationPanel;
private final int[] mInt2Cache = new int[2];
+ private View mBrightnessMirror;
public BrightnessMirrorController(StatusBarWindowView statusBarWindow) {
+ mStatusBarWindow = statusBarWindow;
mScrimBehind = (ScrimView) statusBarWindow.findViewById(R.id.scrim_behind);
mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
inAnimation(mNotificationPanel.animate())
.withLayer()
.withEndAction(new Runnable() {
- @Override
- public void run() {
- mBrightnessMirror.setVisibility(View.INVISIBLE);
- }
- });
+ @Override
+ public void run() {
+ mBrightnessMirror.setVisibility(View.INVISIBLE);
+ }
+ });
}
private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) {
R.integer.notification_panel_layout_gravity);
mBrightnessMirror.setLayoutParams(lp);
}
+
+ public void onDensityOrFontScaleChanged() {
+ int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
+ mStatusBarWindow.removeView(mBrightnessMirror);
+ mBrightnessMirror = LayoutInflater.from(mBrightnessMirror.getContext()).inflate(
+ R.layout.brightness_mirror, mStatusBarWindow, false);
+ mStatusBarWindow.addView(mBrightnessMirror, index);
+ }
}
addView(divider, index);
mDividers.set(i, divider);
}
+ removeView(mOverflowNumber);
+ mOverflowNumber = null;
+ mOverflowInvertHelper = null;
+ mGroupOverFlowState = null;
+ updateGroupOverflow();
}
public void setUserLocked(boolean userLocked) {
public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
mScrimController.setTopHeadsUpDragAmount(animView,
- Math.min(Math.abs(swipeProgress - 1.0f), 1.0f));
+ Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
}
return true; // Don't fade out the notification
}
}
public void setOverflowContainer(NotificationOverflowContainer overFlowContainer) {
+ int index = -1;
+ if (mOverflowContainer != null) {
+ index = indexOfChild(mOverflowContainer);
+ removeView(mOverflowContainer);
+ }
mOverflowContainer = overFlowContainer;
- addView(mOverflowContainer);
+ addView(mOverflowContainer, index);
}
public void updateOverflowContainerVisibility(boolean visible) {
protected void finalize() throws Throwable {
// Clear out the list mClosures to avoid double-destroying the closures,
// in case their finalizers race ahead.
- mClosures.clear();
+ if (mClosures != null) {
+ // ScriptGroup created using the old Builder class does not
+ // initialize the field mClosures
+ mClosures.clear();
+ }
super.finalize();
}
}
if(restoreDefaultNetworkDelayStr != null &&
restoreDefaultNetworkDelayStr.length() != 0) {
try {
- return Integer.valueOf(restoreDefaultNetworkDelayStr);
+ return Integer.parseInt(restoreDefaultNetworkDelayStr);
} catch (NumberFormatException e) {
}
}
FileReader file = new FileReader(DOCK_STATE_PATH);
try {
int len = file.read(buffer, 0, 1024);
- setActualDockStateLocked(Integer.valueOf((new String(buffer, 0, len)).trim()));
+ setActualDockStateLocked(Integer.parseInt((new String(buffer, 0, len)).trim()));
mPreviousDockState = mActualDockState;
} finally {
file.close();
int subtypeId = NOT_A_SUBTYPE_ID;
if (lastIme != null && lastImi != null) {
final boolean imiIdIsSame = lastImi.getId().equals(mCurMethodId);
- final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+ final int lastSubtypeHash = Integer.parseInt(lastIme.second);
final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
: mCurrentSubtype.hashCode();
// If the last IME is the same as the current IME and the last subtype is not
final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
if (lastImi == null) return null;
try {
- final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+ final int lastSubtypeHash = Integer.parseInt(lastIme.second);
final int lastSubtypeId =
InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
if (subtypeHashCode != null) {
try {
lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
- imi, Integer.valueOf(subtypeHashCode));
+ imi, Integer.parseInt(subtypeHashCode));
} catch (NumberFormatException e) {
Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
}
Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
continue;
}
- final int icon = Integer.valueOf(
+ final int icon = Integer.parseInt(
parser.getAttributeValue(null, ATTR_ICON));
- final int label = Integer.valueOf(
+ final int label = Integer.parseInt(
parser.getAttributeValue(null, ATTR_LABEL));
final String imeSubtypeLocale =
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
final String subtypeIdString =
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
if (subtypeIdString != null) {
- builder.setSubtypeId(Integer.valueOf(subtypeIdString));
+ builder.setSubtypeId(Integer.parseInt(subtypeIdString));
}
tempSubtypesArray.add(builder.build());
}
}
@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();
}
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;
}
handleRemoveListLocked();
}
- broadcastCallStateChanged(state, incomingNumber, subId);
+ broadcastCallStateChanged(state, incomingNumber, phoneId, subId);
}
public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) {
}
handleRemoveListLocked();
}
- broadcastServiceStateChanged(state, subId);
- }
-
- public void notifySignalStrength(SignalStrength signalStrength) {
- notifySignalStrengthForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
- signalStrength);
+ broadcastServiceStateChanged(state, phoneId, subId);
}
- 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(
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);
}
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);
}
}
}
} else {
- log("notifySignalStrengthForSubscriber: invalid phoneId=" + phoneId);
+ log("notifySignalStrengthForPhoneId: invalid phoneId=" + phoneId);
}
handleRemoveListLocked();
}
- broadcastSignalStrengthChanged(signalStrength, subId);
+ broadcastSignalStrengthChanged(signalStrength, phoneId, subId);
}
@Override
// 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());
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);
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
* 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) {
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
private int getInt(final String key, final int defaultValue) {
if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
final String result = mCopyOnWriteDataStore.get(key);
- return result != null ? Integer.valueOf(result) : 0;
+ return result != null ? Integer.parseInt(result) : 0;
}
return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
}
FileReader file = new FileReader(uei.getSwitchStatePath());
int len = file.read(buffer, 0, 1024);
file.close();
- curState = Integer.valueOf((new String(buffer, 0, len)).trim());
+ curState = Integer.parseInt((new String(buffer, 0, len)).trim());
if (curState > 0) {
updateStateLocked(uei.getDevPath(), uei.getDevName(), curState);
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;
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;
} else {
mStackSupervisor.resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
- allowResizeInDockedMode);
+ allowResizeInDockedMode, !DEFER_RESUME);
}
}
} finally {
}
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");
}
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);
}
}
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);
}
}
}
return mStackSupervisor.getTopVisibleActivities();
}
}
+
+ @Override
+ public void notifyDockedStackMinimizedChanged(boolean minimized) {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.setDockedStackMinimized(minimized);
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
if (ATTR_ID.equals(attrName)) {
createTime = Long.valueOf(attrValue);
} else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
- launchedFromUid = Integer.valueOf(attrValue);
+ launchedFromUid = Integer.parseInt(attrValue);
} else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
launchedFromPackage = attrValue;
} else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
} else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
componentSpecified = Boolean.valueOf(attrValue);
} else if (ATTR_USERID.equals(attrName)) {
- userId = Integer.valueOf(attrValue);
+ userId = Integer.parseInt(attrValue);
} else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
taskDescription.restoreFromXml(attrName, attrValue);
} else {
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;
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;
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;
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;
// 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<>();
/** 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;
*/
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.
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) {
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
}
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);
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);
}
}
+ 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, onTop /*forceFocus*/,
+ "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");
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);
// 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;
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);
}
// 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
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();
}
}
}
+ 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) {
? 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",
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;
}
/**
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;
ActivityInfo mAInfo;
String mResolvedType;
TaskRecord mInTask;
+ ActivityOptions mActivityOptions;
ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
mService = service;
}
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;
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.
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);
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;
}
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;
inTask = mInterceptor.mInTask;
callingPid = mInterceptor.mCallingPid;
callingUid = mInterceptor.mCallingUid;
-
+ options = mInterceptor.mActivityOptions;
if (abort) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
}
}
+ 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,
package com.android.server.am;
+import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFormatException;
-import android.os.PowerManager;
+import android.os.Parcelable;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.PowerProfile;
-import com.android.internal.telephony.ITelephony;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
+import java.util.concurrent.TimeoutException;
/**
* All information we are collecting about things that can happen that impact
implements PowerManagerInternal.LowPowerModeListener {
static final String TAG = "BatteryStatsService";
- static IBatteryStats sService;
+ /**
+ * How long to wait on an individual subsystem to return its stats.
+ */
+ private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+
+ private static IBatteryStats sService;
+
final BatteryStatsImpl mStats;
- final BatteryStatsHandler mHandler;
- Context mContext;
- PowerManagerInternal mPowerManagerInternal;
+ private final BatteryStatsHandler mHandler;
+ private Context mContext;
+ private IWifiManager mWifiManager;
+ private TelephonyManager mTelephony;
+
+ // Lock acquired when extracting data from external sources.
+ private final Object mExternalStatsLock = new Object();
+
+ // WiFi keeps an accumulated total of stats, unlike Bluetooth.
+ // Keep the last WiFi stats so we can compute a delta.
+ @GuardedBy("mExternalStatsLock")
+ private WifiActivityEnergyInfo mLastInfo =
+ new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
public static final int MSG_SYNC_EXTERNAL_STATS = 1;
public static final int MSG_WRITE_TO_DISK = 2;
+
private int mUpdateFlags = 0;
private IntArray mUidsToRemove = new IntArray();
updateFlags = mUpdateFlags;
mUpdateFlags = 0;
}
- updateExternalStats((String)msg.obj, updateFlags);
+ updateExternalStatsSync((String)msg.obj, updateFlags);
// other parts of the system could be calling into us
// from mStats in order to report of changes. We must grab the mStats
break;
case MSG_WRITE_TO_DISK:
- updateExternalStats("write", UPDATE_ALL);
+ updateExternalStatsSync("write", UPDATE_ALL);
synchronized (mStats) {
mStats.writeAsyncLocked();
}
* initialized. So we initialize the low power observer later.
*/
public void initPowerManagement() {
- mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
- mPowerManagerInternal.registerLowPowerModeObserver(this);
- mStats.notePowerSaveMode(mPowerManagerInternal.getLowPowerModeEnabled());
+ final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
+ powerMgr.registerLowPowerModeObserver(this);
+ mStats.notePowerSaveMode(powerMgr.getLowPowerModeEnabled());
(new WakeupReasonThread()).start();
}
public void shutdown() {
Slog.w("BatteryStats", "Writing battery stats before shutdown...");
- updateExternalStats("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("shutdown", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
mStats.shutdownLocked();
}
//Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
Parcel out = Parcel.obtain();
- updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
mStats.writeToParcel(out, 0);
}
//Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
Parcel out = Parcel.obtain();
- updateExternalStats("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
mStats.writeToParcel(out, 0);
}
}
}
+ @Override
+ public void noteWifiControllerActivity(WifiActivityEnergyInfo info) {
+ enforceCallingPermission();
+
+ if (info == null || !info.isValid()) {
+ Slog.e(TAG, "invalid wifi data given: " + info);
+ return;
+ }
+
+ synchronized (mStats) {
+ mStats.updateWifiStateLocked(info);
+ }
+ }
+
+ @Override
+ public void noteBluetoothControllerActivity(BluetoothActivityEnergyInfo info) {
+ enforceCallingPermission();
+ if (info == null || !info.isValid()) {
+ Slog.e(TAG, "invalid bluetooth data given: " + info);
+ return;
+ }
+
+ synchronized (mStats) {
+ mStats.updateBluetoothStateLocked(info);
+ }
+ }
+
+ @Override
+ public void noteModemControllerActivity(ModemActivityInfo info) {
+ enforceCallingPermission();
+
+ if (info == null || !info.isValid()) {
+ Slog.e(TAG, "invalid modem data given: " + info);
+ return;
+ }
+
+ synchronized (mStats) {
+ mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(), info);
+ }
+ }
+
public boolean isOnBattery() {
return mStats.isOnBattery();
}
// Sync external stats first as the battery has changed states. If we don't sync
// immediately here, we may not collect the relevant data later.
- updateExternalStats("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("battery-state", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
}
pw.println("Battery stats reset.");
noOutput = true;
}
- updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
} else if ("--write".equals(arg)) {
- updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
mStats.writeSyncLocked();
pw.println("Battery stats written.");
flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
}
// Fetch data from external sources and update the BatteryStatsImpl object with them.
- updateExternalStats("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ updateExternalStatsSync("dump", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
- // Objects for extracting data from external sources.
- private final Object mExternalStatsLock = new Object();
-
- @GuardedBy("mExternalStatsLock")
- private IWifiManager mWifiManager;
-
- // WiFi keeps an accumulated total of stats, unlike Bluetooth.
- // Keep the last WiFi stats so we can compute a delta.
- @GuardedBy("mExternalStatsLock")
- private WifiActivityEnergyInfo mLastInfo =
- new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
-
- @GuardedBy("mExternalStatsLock")
- private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() {
- if (mWifiManager == null) {
- mWifiManager = IWifiManager.Stub.asInterface(
- ServiceManager.getService(Context.WIFI_SERVICE));
- if (mWifiManager == null) {
- return null;
- }
- }
-
- try {
- // We read the data even if we are not on battery. This is so that we keep the
- // correct delta from when we should start reading (aka when we are on battery).
- WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo();
- if (info != null && info.isValid()) {
- if (info.mControllerEnergyUsed < 0 || info.mControllerIdleTimeMs < 0 ||
- info.mControllerRxTimeMs < 0 || info.mControllerTxTimeMs < 0) {
- Slog.wtf(TAG, "Reported WiFi energy data is invalid: " + info);
- return null;
- }
-
- final long timePeriodMs = info.mTimestamp - mLastInfo.mTimestamp;
- final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
- final long lastTxMs = mLastInfo.mControllerTxTimeMs;
- final long lastRxMs = mLastInfo.mControllerRxTimeMs;
- final long lastEnergy = mLastInfo.mControllerEnergyUsed;
-
- // We will modify the last info object to be the delta, and store the new
- // WifiActivityEnergyInfo object as our last one.
- final WifiActivityEnergyInfo result = mLastInfo;
- result.mTimestamp = info.getTimeStamp();
- result.mStackState = info.getStackState();
-
- // These times seem to be the most reliable.
- result.mControllerTxTimeMs = info.mControllerTxTimeMs - lastTxMs;
- result.mControllerRxTimeMs = info.mControllerRxTimeMs - lastRxMs;
-
- // WiFi calculates the idle time as a difference from the on time and the various
- // Rx + Tx times. There seems to be some missing time there because this sometimes
- // becomes negative. Just cap it at 0 and move on.
- // b/21613534
- result.mControllerIdleTimeMs = Math.max(0, info.mControllerIdleTimeMs - lastIdleMs);
- result.mControllerEnergyUsed =
- Math.max(0, info.mControllerEnergyUsed - lastEnergy);
-
- if (result.mControllerTxTimeMs < 0 ||
- result.mControllerRxTimeMs < 0) {
- // The stats were reset by the WiFi system (which is why our delta is negative).
- // Returns the unaltered stats.
- result.mControllerEnergyUsed = info.mControllerEnergyUsed;
- result.mControllerRxTimeMs = info.mControllerRxTimeMs;
- result.mControllerTxTimeMs = info.mControllerTxTimeMs;
- result.mControllerIdleTimeMs = info.mControllerIdleTimeMs;
-
- Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
- }
-
- // There is some accuracy error in reports so allow some slop in the results.
- final long SAMPLE_ERROR_MILLIS = 750;
- final long totalTimeMs = result.mControllerIdleTimeMs + result.mControllerRxTimeMs +
- result.mControllerTxTimeMs;
- if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
- StringBuilder sb = new StringBuilder();
- sb.append("Total time ");
- TimeUtils.formatDuration(totalTimeMs, sb);
- sb.append(" is longer than sample period ");
- TimeUtils.formatDuration(timePeriodMs, sb);
- sb.append(".\n");
- sb.append("Previous WiFi snapshot: ").append("idle=");
- TimeUtils.formatDuration(lastIdleMs, sb);
- sb.append(" rx=");
- TimeUtils.formatDuration(lastRxMs, sb);
- sb.append(" tx=");
- TimeUtils.formatDuration(lastTxMs, sb);
- sb.append(" e=").append(lastEnergy);
- sb.append("\n");
- sb.append("Current WiFi snapshot: ").append("idle=");
- TimeUtils.formatDuration(info.mControllerIdleTimeMs, sb);
- sb.append(" rx=");
- TimeUtils.formatDuration(info.mControllerRxTimeMs, sb);
- sb.append(" tx=");
- TimeUtils.formatDuration(info.mControllerTxTimeMs, sb);
- sb.append(" e=").append(info.mControllerEnergyUsed);
- Slog.wtf(TAG, sb.toString());
- }
-
- mLastInfo = info;
- return result;
- }
- } catch (RemoteException e) {
- // Nothing to report, WiFi is dead.
- }
- return null;
+ private WifiActivityEnergyInfo extractDelta(WifiActivityEnergyInfo latest) {
+ final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
+ final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
+ final long lastTxMs = mLastInfo.mControllerTxTimeMs;
+ final long lastRxMs = mLastInfo.mControllerRxTimeMs;
+ final long lastEnergy = mLastInfo.mControllerEnergyUsed;
+
+ // We will modify the last info object to be the delta, and store the new
+ // WifiActivityEnergyInfo object as our last one.
+ final WifiActivityEnergyInfo delta = mLastInfo;
+ delta.mTimestamp = latest.getTimeStamp();
+ delta.mStackState = latest.getStackState();
+
+ // These times seem to be the most reliable.
+ delta.mControllerTxTimeMs = latest.mControllerTxTimeMs - lastTxMs;
+ delta.mControllerRxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
+
+ // WiFi calculates the idle time as a difference from the on time and the various
+ // Rx + Tx times. There seems to be some missing time there because this sometimes
+ // becomes negative. Just cap it at 0 and move on.
+ // b/21613534
+ delta.mControllerIdleTimeMs = Math.max(0, latest.mControllerIdleTimeMs - lastIdleMs);
+ delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
+
+ if (delta.mControllerTxTimeMs < 0 || delta.mControllerRxTimeMs < 0) {
+ // The stats were reset by the WiFi system (which is why our delta is negative).
+ // Returns the unaltered stats.
+ delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
+ delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
+ delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
+ delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+
+ Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
+ }
+
+ // There is some accuracy error in reports so allow some slop in the results.
+ final long SAMPLE_ERROR_MILLIS = 750;
+ final long totalTimeMs = delta.mControllerIdleTimeMs + delta.mControllerRxTimeMs +
+ delta.mControllerTxTimeMs;
+ if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Total time ");
+ TimeUtils.formatDuration(totalTimeMs, sb);
+ sb.append(" is longer than sample period ");
+ TimeUtils.formatDuration(timePeriodMs, sb);
+ sb.append(".\n");
+ sb.append("Previous WiFi snapshot: ").append("idle=");
+ TimeUtils.formatDuration(lastIdleMs, sb);
+ sb.append(" rx=");
+ TimeUtils.formatDuration(lastRxMs, sb);
+ sb.append(" tx=");
+ TimeUtils.formatDuration(lastTxMs, sb);
+ sb.append(" e=").append(lastEnergy);
+ sb.append("\n");
+ sb.append("Current WiFi snapshot: ").append("idle=");
+ TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
+ sb.append(" rx=");
+ TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
+ sb.append(" tx=");
+ TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
+ sb.append(" e=").append(latest.mControllerEnergyUsed);
+ Slog.wtf(TAG, sb.toString());
+ }
+
+ mLastInfo = latest;
+ return delta;
}
- @GuardedBy("mExternalStatsLock")
- private BluetoothActivityEnergyInfo pullBluetoothEnergyInfoLocked() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
- BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
- if (info != null && info.isValid()) {
- if (info.getControllerEnergyUsed() < 0 || info.getControllerIdleTimeMillis() < 0 ||
- info.getControllerRxTimeMillis() < 0 || info.getControllerTxTimeMillis() < 0) {
- Slog.wtf(TAG, "Bluetooth energy data is invalid: " + info);
- }
- return info;
- }
+ /**
+ * Helper method to extract the Parcelable controller info from a
+ * SynchronousResultReceiver.
+ */
+ private static <T extends Parcelable> T awaitControllerInfo(
+ @Nullable SynchronousResultReceiver receiver) throws TimeoutException {
+ if (receiver == null) {
+ return null;
}
- return null;
- }
- @GuardedBy("mExternalStatsLock")
- private ModemActivityInfo pullModemActivityInfoLocked() {
- ITelephony tm = ITelephony.Stub.asInterface(ServiceManager.getService(
- Context.TELEPHONY_SERVICE));
- try {
- if (tm != null) {
- ModemActivityInfo info = tm.getModemActivityInfo();
- if (info == null || info.isValid()) {
- return info;
- }
- Slog.wtf(TAG, "Modem activity info is invalid: " + info);
+ final SynchronousResultReceiver.Result result =
+ receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
+ if (result.bundle != null) {
+ // This is the final destination for the Bundle.
+ result.bundle.setDefusable(true);
+
+ final T data = result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY);
+ if (data != null) {
+ return data;
}
- } catch (RemoteException e) {
- // Nothing to do.
}
+ Slog.e(TAG, "no controller energy info supplied");
return null;
}
* {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_WIFI},
* and {@link BatteryStatsImpl.ExternalStatsSync#UPDATE_BT}.
*/
- void updateExternalStats(final String reason, final int updateFlags) {
+ void updateExternalStatsSync(final String reason, int updateFlags) {
+ SynchronousResultReceiver wifiReceiver = null;
+ SynchronousResultReceiver bluetoothReceiver = null;
+ SynchronousResultReceiver modemReceiver = null;
+
synchronized (mExternalStatsLock) {
if (mContext == null) {
- // We haven't started yet (which means the BatteryStatsImpl object has
- // no power profile. Don't consume data we can't compute yet.
+ // Don't do any work yet.
return;
}
- if (BatteryStatsImpl.DEBUG_ENERGY) {
- Slog.d(TAG, "Updating external stats: reason=" + reason);
- }
-
- WifiActivityEnergyInfo wifiEnergyInfo = null;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
- wifiEnergyInfo = pullWifiEnergyInfoLocked();
- }
+ if (mWifiManager == null) {
+ mWifiManager = IWifiManager.Stub.asInterface(
+ ServiceManager.getService(Context.WIFI_SERVICE));
+ }
- ModemActivityInfo modemActivityInfo = null;
- if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
- modemActivityInfo = pullModemActivityInfoLocked();
+ if (mWifiManager != null) {
+ try {
+ wifiReceiver = new SynchronousResultReceiver();
+ mWifiManager.requestActivityInfo(wifiReceiver);
+ } catch (RemoteException e) {
+ // Oh well.
+ }
+ }
}
- BluetoothActivityEnergyInfo bluetoothEnergyInfo = null;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
- // We only pull bluetooth stats when we have to, as we are not distributing its
- // use amongst apps and the sampling frequency does not matter.
- bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ bluetoothReceiver = new SynchronousResultReceiver();
+ adapter.requestControllerActivityEnergyInfo(
+ BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED,
+ bluetoothReceiver);
+ }
}
- synchronized (mStats) {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long uptime = SystemClock.uptimeMillis();
- if (mStats.mRecordAllHistory) {
- mStats.addHistoryEventLocked(elapsedRealtime, uptime,
- BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, reason, 0);
+ if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
+ if (mTelephony == null) {
+ mTelephony = TelephonyManager.from(mContext);
}
- if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU) != 0) {
- mStats.updateCpuTimeLocked();
- mStats.updateKernelWakelocksLocked();
+ if (mTelephony != null) {
+ modemReceiver = new SynchronousResultReceiver();
+ mTelephony.requestModemActivityInfo(modemReceiver);
}
+ }
- if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
- mStats.updateMobileRadioStateLocked(elapsedRealtime, modemActivityInfo);
+ WifiActivityEnergyInfo wifiInfo = null;
+ BluetoothActivityEnergyInfo bluetoothInfo = null;
+ ModemActivityInfo modemInfo = null;
+ try {
+ wifiInfo = awaitControllerInfo(wifiReceiver);
+ } catch (TimeoutException e) {
+ Slog.w(TAG, "Timeout reading wifi stats");
+ }
+
+ try {
+ bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
+ } catch (TimeoutException e) {
+ Slog.w(TAG, "Timeout reading bt stats");
+ }
+
+ try {
+ modemInfo = awaitControllerInfo(modemReceiver);
+ } catch (TimeoutException e) {
+ Slog.w(TAG, "Timeout reading modem stats");
+ }
+
+ synchronized (mStats) {
+ mStats.addHistoryEventLocked(
+ SystemClock.elapsedRealtime(),
+ SystemClock.uptimeMillis(),
+ BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
+ reason, 0);
+
+ mStats.updateCpuTimeLocked();
+ mStats.updateKernelWakelocksLocked();
+
+ if (wifiInfo != null) {
+ if (wifiInfo.isValid()) {
+ mStats.updateWifiStateLocked(extractDelta(wifiInfo));
+ } else {
+ Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
+ }
}
- if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
- mStats.updateWifiStateLocked(wifiEnergyInfo);
+ if (bluetoothInfo != null) {
+ if (bluetoothInfo.isValid()) {
+ mStats.updateBluetoothStateLocked(bluetoothInfo);
+ } else {
+ Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
+ }
}
- if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
- mStats.updateBluetoothStateLocked(bluetoothEnergyInfo);
+ if (modemInfo != null) {
+ if (modemInfo.isValid()) {
+ mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime(),
+ modemInfo);
+ } else {
+ Slog.e(TAG, "modem info is invalid: " + modemInfo);
+ }
}
}
}
}
long ident = Binder.clearCallingIdentity();
try {
- updateExternalStats("get-health-stats-for-uid",
+ updateExternalStatsSync("get-health-stats-for-uid",
BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
return getHealthStatsForUidLocked(requestUid);
long ident = Binder.clearCallingIdentity();
int i=-1;
try {
- updateExternalStats("get-health-stats-for-uids",
+ updateExternalStatsSync("get-health-stats-for-uids",
BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
synchronized (mStats) {
final int N = requestUids.length;
/**
* Gets a HealthStatsParceler for the given uid. You should probably call
- * updateExternalStats first.
+ * updateExternalStatsSync first.
*/
HealthStatsParceler getHealthStatsForUidLocked(int requestUid) {
final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter();
if (taskIdEnd > 0) {
final int taskId;
try {
- taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
+ taskId = Integer.parseInt(filename.substring(0, taskIdEnd));
if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: Found taskId=" + taskId);
} catch (Exception e) {
Slog.wtf(TAG, "removeObsoleteFiles: Can't parse file=" + file.getName());
if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
attrName + " value=" + attrValue);
if (ATTR_TASKID.equals(attrName)) {
- if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
+ if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
} else if (ATTR_REALACTIVITY.equals(attrName)) {
realActivity = ComponentName.unflattenFromString(attrValue);
} else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
} else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
askedCompatMode = Boolean.valueOf(attrValue);
} else if (ATTR_USERID.equals(attrName)) {
- userId = Integer.valueOf(attrValue);
+ userId = Integer.parseInt(attrValue);
} else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
userSetupComplete = Boolean.valueOf(attrValue);
} else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
- effectiveUid = Integer.valueOf(attrValue);
+ effectiveUid = Integer.parseInt(attrValue);
} else if (ATTR_TASKTYPE.equals(attrName)) {
- taskType = Integer.valueOf(attrValue);
+ taskType = Integer.parseInt(attrValue);
} else if (ATTR_FIRSTACTIVETIME.equals(attrName)) {
firstActiveTime = Long.valueOf(attrValue);
} else if (ATTR_LASTACTIVETIME.equals(attrName)) {
} else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
taskDescription.restoreFromXml(attrName, attrValue);
} else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
- taskAffiliation = Integer.valueOf(attrValue);
+ taskAffiliation = Integer.parseInt(attrValue);
} else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
- prevTaskId = Integer.valueOf(attrValue);
+ prevTaskId = Integer.parseInt(attrValue);
} else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
- nextTaskId = Integer.valueOf(attrValue);
+ nextTaskId = Integer.parseInt(attrValue);
} else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
- taskAffiliationColor = Integer.valueOf(attrValue);
+ taskAffiliationColor = Integer.parseInt(attrValue);
} else if (ATTR_CALLING_UID.equals(attrName)) {
- callingUid = Integer.valueOf(attrValue);
+ callingUid = Integer.parseInt(attrValue);
} else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
callingPackage = attrValue;
} else if (ATTR_RESIZE_MODE.equals(attrName)) {
- resizeMode = Integer.valueOf(attrValue);
+ resizeMode = Integer.parseInt(attrValue);
resizeMode = (resizeMode == RESIZE_MODE_CROP_WINDOWS)
? RESIZE_MODE_FORCE_RESIZEABLE : resizeMode;
} else if (ATTR_PRIVILEGED.equals(attrName)) {
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
bounds = Rect.unflattenFromString(attrValue);
} else if (ATTR_MINIMAL_WIDTH.equals(attrName)) {
- minimalWidth = Integer.valueOf(attrValue);
+ minimalWidth = Integer.parseInt(attrValue);
} else if (ATTR_MINIMAL_HEIGHT.equals(attrName)) {
- minimalHeight = Integer.valueOf(attrValue);
+ minimalHeight = Integer.parseInt(attrValue);
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
mMeasurement.description += " src{" + getSocketAddressString() + "}";
// This needs to be fixed length so it can be dropped into the pre-canned packet.
- final String sixRandomDigits =
- Integer.valueOf(mRandom.nextInt(900000) + 100000).toString();
+ final String sixRandomDigits = String.valueOf(mRandom.nextInt(900000) + 100000);
mMeasurement.description += " qtype{" + mQueryType + "}"
+ " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
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);
final List<JobStatus> jobs = new ArrayList<JobStatus>();
// Read in version info.
try {
- int version = Integer.valueOf(parser.getAttributeValue(null, "version"));
+ int version = Integer.parseInt(parser.getAttributeValue(null, "version"));
if (version != JOBS_FILE_VERSION) {
Slog.d(TAG, "Invalid version number, aborting jobs file read.");
return null;
try {
jobBuilder = buildBuilderFromXml(parser);
jobBuilder.setPersisted(true);
- uid = Integer.valueOf(parser.getAttributeValue(null, "uid"));
+ uid = Integer.parseInt(parser.getAttributeValue(null, "uid"));
String val = parser.getAttributeValue(null, "priority");
if (val != null) {
- jobBuilder.setPriority(Integer.valueOf(val));
+ jobBuilder.setPriority(Integer.parseInt(val));
}
val = parser.getAttributeValue(null, "sourceUserId");
- sourceUserId = val == null ? -1 : Integer.valueOf(val);
+ sourceUserId = val == null ? -1 : Integer.parseInt(val);
} catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing job's required fields, skipping");
return null;
private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
// Pull out required fields from <job> attributes.
- int jobId = Integer.valueOf(parser.getAttributeValue(null, "jobid"));
+ int jobId = Integer.parseInt(parser.getAttributeValue(null, "jobid"));
String packageName = parser.getAttributeValue(null, "package");
String className = parser.getAttributeValue(null, "class");
ComponentName cname = new ComponentName(packageName, className);
if (val != null) {
long initialBackoff = Long.valueOf(val);
val = parser.getAttributeValue(null, "backoff-policy");
- int backoffPolicy = Integer.valueOf(val); // Will throw NFE which we catch higher up.
+ int backoffPolicy = Integer.parseInt(val); // Will throw NFE which we catch higher up.
jobBuilder.setBackoffCriteria(initialBackoff, backoffPolicy);
}
}
private static int tryParseInt(String value, int defValue) {
if (TextUtils.isEmpty(value)) return defValue;
try {
- return Integer.valueOf(value);
+ return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defValue;
}
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 {
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;
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);
}
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
}
/**
+ * 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;
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);
}
}
}
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);
}
}
* 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();
/** 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) {
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;
}
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;
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);
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) {
}
/**
- * 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) {
*/
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);
}
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.WindowManager.DOCKED_TOP;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@Override
public View addStartingWindow(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
- int icon, int logo, int windowFlags) {
+ int icon, int logo, int windowFlags, Configuration overrideConfig) {
if (!SHOW_STARTING_ANIMATIONS) {
return null;
}
}
}
- PhoneWindow win = new PhoneWindow(context);
+ if (overrideConfig != null && overrideConfig != EMPTY) {
+ if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based"
+ + " on overrideConfig" + overrideConfig + " for starting window");
+ final Context overrideContext = context.createConfigurationContext(overrideConfig);
+ overrideContext.setTheme(theme);
+ final TypedArray typedArray = overrideContext.obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
+ if (resId != 0 && overrideContext.getDrawable(resId) != null) {
+ // We want to use the windowBackground for the override context if it is
+ // available, otherwise we use the default one to make sure a themed starting
+ // window is displayed for the app.
+ if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig"
+ + overrideConfig + " to starting window resId=" + resId);
+ context = overrideContext;
+ }
+ }
+
+ final PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
- Resources r = context.getResources();
+ final Resources r = context.getResources();
win.setTitle(r.getText(labelRes, nonLocalizedLabel));
win.setType(
wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
view = win.getDecorView();
- if (DEBUG_STARTING_WINDOW) Slog.d(
- TAG, "Adding starting window for " + packageName
- + " / " + appToken + ": "
- + (view.getParent() != null ? view : null));
+ if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for "
+ + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
wm.addView(view, params);
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;
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;
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;
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);
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
}
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) {
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);
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");
+ setVrComponentsEnabledOrDisabled(vrComponents, false);
+
+ // 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 void setVrComponentsEnabledOrDisabled(ArraySet<ComponentName> vrComponents,
+ boolean enabled) {
+ int state = enabled ?
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ PackageManager pm = mContext.getPackageManager();
+ for (ComponentName componentName : vrComponents) {
+ try {
+ // Note that we must first check for the existance of the package before trying
+ // to set its enabled state. This is to prevent PackageManager from throwing
+ // an excepton if the package is not found (not just a NameNotFoundException
+ // exception).
+ PackageInfo packageInfo = pm.getPackageInfo(componentName.getPackageName(),
+ PackageManager.GET_CONFIGURATIONS);
+ pm.setApplicationEnabledSetting(componentName.getPackageName(), state , 0);
+ } catch (NameNotFoundException e) {
+ }
+ }
+ }
+
+ 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");
+ setVrComponentsEnabledOrDisabled(vrComponents, true);
+ return true;
+ }
+ }
+ }
+ return false;
}
@Override
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);
}
}
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);
}
}
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;
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;
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() {
return mDividerInsets;
}
+ int getContentWidthInactive() {
+ return mDividerWindowWidthInactive;
+ }
+
void setResizing(boolean resizing) {
if (mResizing != resizing) {
mResizing = resizing;
}
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);
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.
public String toShortString() {
return TAG;
}
-}
+}
\ No newline at end of file
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;
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 */);
}
mImeGoingAway = false;
mAdjustImeAmount = 0f;
updateAdjustedBounds();
+ mService.setResizeDimLayer(false, mStackId, 1.0f);
} else {
mImeGoingAway |= mAdjustedForIme;
}
}
}
+ 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;
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);
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() {
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
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);
}
}
return mCurrentFocus;
}
+ TaskStack getFocusedStackLocked() {
+ return mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+ }
+
private void showAuditSafeModeNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
new Intent(Intent.ACTION_VIEW,
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.
*/
View view = null;
try {
- view = mPolicy.addStartingWindow(
- wtoken.token, sd.pkg, sd.theme, sd.compatInfo,
- sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo, sd.windowFlags);
+ final Configuration overrideConfig = wtoken != null && wtoken.mTask != null
+ ? wtoken.mTask.mOverrideConfig : null;
+ view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
+ sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
+ sd.windowFlags, overrideConfig);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when adding starting window", e);
}
}
}
break;
+ case NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED: {
+ mAmInternal.notifyDockedStackMinimizedChanged(msg.arg1 == 1);
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
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);
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.
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;
}
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;
private static final boolean VDBG = false;
private static final int ETH_HEADER_LEN = 14;
+ private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
* DROP_LABEL or PASS_LABEL and does not fall off the end.
* Preconditions:
* - Packet being filtered is IPv4
- * - R1 is initialized to 0
*/
@GuardedBy("this")
private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
// Drop all broadcasts besides DHCP addressed to us
// If not a broadcast packet, pass
- // NOTE: Relies on R1 being initialized to 0 which is the offset of the ethernet
- // destination MAC address
- gen.addJumpIfBytesNotEqual(Register.R1, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+ gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
// If not UDP, drop
gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
* Add an instruction to the end of the program to jump to {@code target} if the bytes of the
* packet at, an offset specified by {@code register}, match {@code bytes}.
*/
- public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target) {
+ public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
+ throws IllegalInstructionException {
+ if (register == Register.R1) {
+ throw new IllegalInstructionException("JNEBS fails with R1");
+ }
Instruction instruction = new Instruction(Opcodes.JNEBS, register);
instruction.setUnsignedImm(bytes.length);
instruction.setTargetLabel(target);
}
// Process existing model first.
- if (model != null && model.getModelId() != soundModel.uuid) {
+ if (model != null && !model.getModelId().equals(soundModel.uuid)) {
// The existing model has a different UUID, should be replaced.
int status = cleanUpExistingKeyphraseModel(model);
- removeKeyphraseModelLocked(keyphraseId);
if (status != STATUS_OK) {
return status;
}
+ removeKeyphraseModelLocked(keyphraseId);
model = null;
}
} else {
// Clear the ModelData state if successful.
modelData.clearState();
- modelData.clearCallback();
- modelData.setRecognitionConfig(null);
}
}
return status;
// Stop all recognition models.
for (ModelData model : mModelDataMap.values()) {
if (model.isModelStarted()) {
- model.setRequested(false);
int status = stopRecognitionLocked(model,
false /* do not notify for synchronous calls */);
if (status != STATUS_OK) {
Slog.w(TAG, "Error stopping keyphrase model: " + model.getHandle());
}
model.clearState();
- model.clearCallback();
- model.setRecognitionConfig(null);
}
}
internalClearGlobalStateLocked();
private void internalClearModelStateLocked() {
for (ModelData modelData : mModelDataMap.values()) {
modelData.clearState();
- modelData.clearCallback();
}
}
synchronized void clearState() {
mModelState = MODEL_NOTLOADED;
mModelHandle = INVALID_VALUE;
+ mRecognitionConfig = null;
+ mRequested = false;
+ mCallback = null;
}
synchronized void clearCallback() {
String[] usersStr = text.split(",");
int[] users = new int[usersStr.length];
for (int i = 0; i < usersStr.length; i++) {
- users[i] = Integer.valueOf(usersStr[i]);
+ users[i] = Integer.parseInt(usersStr[i]);
}
return users;
}
"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.
*/
*/
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.
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);
mNetworkType = radioType;
// check if 0xFFFFFFFF for UNKNOWN_CID
if (!location.equalsIgnoreCase("FFFFFFFF")) {
- mCid = Integer.valueOf(location.substring(4), 16);
- mLac = Integer.valueOf(location.substring(0, 4), 16);
+ mCid = Integer.parseInt(location.substring(4), 16);
+ mLac = Integer.parseInt(location.substring(0, 4), 16);
}
break;
case NETWORK_TYPE_UMTS:
case NETWORK_TYPE_HSUPA:
case NETWORK_TYPE_HSPA:
mNetworkType = radioType;
- mPsc = Integer.valueOf(location, 16);
+ mPsc = Integer.parseInt(location, 16);
break;
}
} catch (NumberFormatException e) {
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();
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.BatteryStats;
+import android.os.ResultReceiver;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.os.Bundle;
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
+ /**
+ * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
+ * into the ResultReceiver Bundle.
+ * @hide
+ */
+ public static final String MODEM_ACTIVITY_RESULT_KEY =
+ BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
+
private static ITelephonyRegistry sRegistry;
/**
}
/**
- * Returns the modem activity info.
+ * Requests the modem activity info. The recipient will place the result
+ * in `result`.
+ * @param result The object on which the recipient will send the resulting
+ * {@link android.telephony.ModemActivityInfo} object.
* @hide
*/
- public ModemActivityInfo getModemActivityInfo() {
+ public void requestModemActivityInfo(ResultReceiver result) {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.getModemActivityInfo();
+ service.requestModemActivityInfo(result);
+ return;
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
}
- return null;
+ result.send(0, null);
}
/**
import android.content.Intent;
import android.os.Bundle;
+import android.os.ResultReceiver;
import android.net.Uri;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
String getLocaleFromDefaultSim();
/**
- * Return the modem activity info.
+ * Requests the modem activity info asynchronously.
+ * The implementor is expected to reply with the
+ * {@link android.telephony.ModemActivityInfo} object placed into the Bundle with the key
+ * {@link android.telephony.TelephonyManager#MODEM_ACTIVITY_RESULT_KEY}.
+ * The result code is ignored.
*/
- ModemActivityInfo getModemActivityInfo();
+ oneway void requestModemActivityInfo(in ResultReceiver result);
/**
* Get the service state on specified subscription
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);
if (testID.startsWith("test")) {
- testNum = Integer.valueOf(testID.substring(4))-1;
+ testNum = Integer.parseInt(testID.substring(4))-1;
}
if ((testNum < 0) || (testNum > TestWebData.tests.length - 1)) {
@Override
public void setElapsedFrameTimeNanos(long nanos) {
- mSession.setElapsedFrameTimeNanos(nanos);
+ if (mSession != null) {
+ mSession.setElapsedFrameTimeNanos(nanos);
+ }
}
@Override
private List<ViewInfo> mViewInfoList;
private List<ViewInfo> mSystemViewInfoList;
private Layout.Builder mLayoutBuilder;
+ private boolean mNewRenderSize;
private static final class PostInflateException extends Exception {
private static final long serialVersionUID = 1L;
}
/**
+ * Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
+ */
+ private void measure(@NonNull SessionParams params) {
+ // only do the screen measure when needed.
+ if (mMeasuredScreenWidth != -1) {
+ return;
+ }
+
+ RenderingMode renderingMode = params.getRenderingMode();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
+
+ mNewRenderSize = true;
+ mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
+ mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
+
+ if (renderingMode != RenderingMode.NORMAL) {
+ int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY;
+ int heightMeasureSpecMode = renderingMode.isVertExpand() ?
+ MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+ : MeasureSpec.EXACTLY;
+
+ // We used to compare the measured size of the content to the screen size but
+ // this does not work anymore due to the 2 following issues:
+ // - If the content is in a decor (system bar, title/action bar), the root view
+ // will not resize even with the UNSPECIFIED because of the embedded layout.
+ // - If there is no decor, but a dialog frame, then the dialog padding prevents
+ // comparing the size of the content to the screen frame (as it would not
+ // take into account the dialog padding).
+
+ // The solution is to first get the content size in a normal rendering, inside
+ // the decor or the dialog padding.
+ // Then measure only the content with UNSPECIFIED to see the size difference
+ // and apply this to the screen size.
+
+ // first measure the full layout, with EXACTLY to get the size of the
+ // content as it is inside the decor/dialog
+ @SuppressWarnings("deprecation")
+ Pair<Integer, Integer> exactMeasure = measureView(
+ mViewRoot, mContentRoot.getChildAt(0),
+ mMeasuredScreenWidth, MeasureSpec.EXACTLY,
+ mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+
+ // now measure the content only using UNSPECIFIED (where applicable, based on
+ // the rendering mode). This will give us the size the content needs.
+ @SuppressWarnings("deprecation")
+ Pair<Integer, Integer> result = measureView(
+ mContentRoot, mContentRoot.getChildAt(0),
+ mMeasuredScreenWidth, widthMeasureSpecMode,
+ mMeasuredScreenHeight, heightMeasureSpecMode);
+
+ // now look at the difference and add what is needed.
+ if (renderingMode.isHorizExpand()) {
+ int measuredWidth = exactMeasure.getFirst();
+ int neededWidth = result.getFirst();
+ if (neededWidth > measuredWidth) {
+ mMeasuredScreenWidth += neededWidth - measuredWidth;
+ }
+ if (mMeasuredScreenWidth < measuredWidth) {
+ // If the screen width is less than the exact measured width,
+ // expand to match.
+ mMeasuredScreenWidth = measuredWidth;
+ }
+ }
+
+ if (renderingMode.isVertExpand()) {
+ int measuredHeight = exactMeasure.getSecond();
+ int neededHeight = result.getSecond();
+ if (neededHeight > measuredHeight) {
+ mMeasuredScreenHeight += neededHeight - measuredHeight;
+ }
+ if (mMeasuredScreenHeight < measuredHeight) {
+ // If the screen height is less than the exact measured height,
+ // expand to match.
+ mMeasuredScreenHeight = measuredHeight;
+ }
+ }
+ }
+ }
+
+ /**
* Inflates the layout.
* <p>
* {@link #acquire(long)} must have been called before this.
setActiveToolbar(view, context, params);
+ measure(params);
+ measureView(mViewRoot, null /*measuredView*/,
+ mMeasuredScreenWidth, MeasureSpec.EXACTLY,
+ mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+ mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+ mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
+ false);
+
return SUCCESS.createResult();
} catch (PostInflateException e) {
return ERROR_INFLATION.createResult(e.getMessage(), e);
return ERROR_NOT_INFLATED.createResult();
}
- RenderingMode renderingMode = params.getRenderingMode();
- HardwareConfig hardwareConfig = params.getHardwareConfig();
-
- // only do the screen measure when needed.
- boolean newRenderSize = false;
- if (mMeasuredScreenWidth == -1) {
- newRenderSize = true;
- mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
- mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
-
- if (renderingMode != RenderingMode.NORMAL) {
- int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
- MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
- : MeasureSpec.EXACTLY;
- int heightMeasureSpecMode = renderingMode.isVertExpand() ?
- MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
- : MeasureSpec.EXACTLY;
-
- // We used to compare the measured size of the content to the screen size but
- // this does not work anymore due to the 2 following issues:
- // - If the content is in a decor (system bar, title/action bar), the root view
- // will not resize even with the UNSPECIFIED because of the embedded layout.
- // - If there is no decor, but a dialog frame, then the dialog padding prevents
- // comparing the size of the content to the screen frame (as it would not
- // take into account the dialog padding).
-
- // The solution is to first get the content size in a normal rendering, inside
- // the decor or the dialog padding.
- // Then measure only the content with UNSPECIFIED to see the size difference
- // and apply this to the screen size.
-
- // first measure the full layout, with EXACTLY to get the size of the
- // content as it is inside the decor/dialog
- @SuppressWarnings("deprecation")
- Pair<Integer, Integer> exactMeasure = measureView(
- mViewRoot, mContentRoot.getChildAt(0),
- mMeasuredScreenWidth, MeasureSpec.EXACTLY,
- mMeasuredScreenHeight, MeasureSpec.EXACTLY);
-
- // now measure the content only using UNSPECIFIED (where applicable, based on
- // the rendering mode). This will give us the size the content needs.
- @SuppressWarnings("deprecation")
- Pair<Integer, Integer> result = measureView(
- mContentRoot, mContentRoot.getChildAt(0),
- mMeasuredScreenWidth, widthMeasureSpecMode,
- mMeasuredScreenHeight, heightMeasureSpecMode);
-
- // now look at the difference and add what is needed.
- if (renderingMode.isHorizExpand()) {
- int measuredWidth = exactMeasure.getFirst();
- int neededWidth = result.getFirst();
- if (neededWidth > measuredWidth) {
- mMeasuredScreenWidth += neededWidth - measuredWidth;
- }
- if (mMeasuredScreenWidth < measuredWidth) {
- // If the screen width is less than the exact measured width,
- // expand to match.
- mMeasuredScreenWidth = measuredWidth;
- }
- }
-
- if (renderingMode.isVertExpand()) {
- int measuredHeight = exactMeasure.getSecond();
- int neededHeight = result.getSecond();
- if (neededHeight > measuredHeight) {
- mMeasuredScreenHeight += neededHeight - measuredHeight;
- }
- if (mMeasuredScreenHeight < measuredHeight) {
- // If the screen height is less than the exact measured height,
- // expand to match.
- mMeasuredScreenHeight = measuredHeight;
- }
- }
- }
- }
+ measure(params);
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
Result renderResult = SUCCESS.createResult();
if (params.isLayoutOnly()) {
// delete the canvas and image to reset them on the next full rendering
// it doesn't get cached.
boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
- if (newRenderSize || mCanvas == null || disableBitmapCaching) {
+ if (mNewRenderSize || mCanvas == null || disableBitmapCaching) {
+ mNewRenderSize = false;
if (params.getImageFactory() != null) {
mImage = params.getImageFactory().getImage(
mMeasuredScreenWidth,
import android.net.DhcpInfo;
-
import android.os.Messenger;
+import android.os.ResultReceiver;
import android.os.WorkSource;
/**
WifiActivityEnergyInfo reportActivityInfo();
+ /**
+ * Requests the controller activity info asynchronously.
+ * The implementor is expected to reply with the
+ * {@link android.net.wifi.WifiActivityEnergyInfo} object placed into the Bundle with the key
+ * {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. The result code is ignored.
+ */
+ oneway void requestActivityInfo(in ResultReceiver result);
+
List<WifiConfiguration> getConfiguredNetworks();
List<WifiConfiguration> getPrivilegedConfiguredNetworks();
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;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
-import java.util.concurrent.CountDownLatch;
-
/** @hide */
@SystemApi
public class RttManager {
}
public RttCapabilities getRttCapabilities() {
- synchronized (sCapabilitiesLock) {
+ synchronized (mCapabilitiesLock) {
if (mRttCapabilities == null) {
try {
mRttCapabilities = mService.getRttCapabilities();
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));
}
/**
}
validateChannel();
int key = putListenerIfAbsent(callback);
- sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
}
/**
Log.e(TAG, "responder not enabled yet");
return;
}
- sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
}
/**
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.
* 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 */
- }
+ Messenger messenger = null;
+ try {
+ Log.d(TAG, "Get the messenger from " + mService);
+ messenger = mService.getMessenger();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
- if (messenger == null) {
- sAsyncChannel = null;
- return;
- }
+ if (messenger == null) {
+ throw new IllegalStateException("getMessenger() returned null! This is invalid.");
+ }
- sHandlerThread = new HandlerThread("RttManager");
- sAsyncChannel = new AsyncChannel();
- sConnected = new CountDownLatch(1);
+ mAsyncChannel = new AsyncChannel();
- 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");
- }
- }
- }
+ 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);
}
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;
}
* @return if the record is valid
*/
public boolean isValid() {
- return ((mControllerTxTimeMs !=0) ||
- (mControllerRxTimeMs !=0) ||
- (mControllerIdleTimeMs !=0));
+ return ((mControllerTxTimeMs >=0) &&
+ (mControllerRxTimeMs >=0) &&
+ (mControllerIdleTimeMs >=0));
}
}