diff options
118 files changed, 3569 insertions, 1659 deletions
diff --git a/Android.mk b/Android.mk index f0c0117bf44a..71bba0f16ea9 100644 --- a/Android.mk +++ b/Android.mk @@ -259,6 +259,7 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityManager.aidl \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ + core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \ core/java/android/view/IAssetAtlas.aidl \ core/java/android/view/IGraphicsStats.aidl \ core/java/android/view/IInputFilter.aidl \ diff --git a/api/current.txt b/api/current.txt index 3e63cd7237e1..73b522fa0aca 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5730,6 +5730,7 @@ package android.app.admin { method public int getStorageEncryptionStatus(); method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy(); method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName); + method public android.os.Bundle getUserRestrictions(android.content.ComponentName); method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]); method public boolean hasGrantedPolicy(android.content.ComponentName, int); method public boolean installCaCert(android.content.ComponentName, byte[]); diff --git a/api/system-current.txt b/api/system-current.txt index bd674ef65f0b..87932ac9617b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5860,6 +5860,7 @@ package android.app.admin { method public int getStorageEncryptionStatus(); method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy(); method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName); + method public android.os.Bundle getUserRestrictions(android.content.ComponentName); method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]); method public boolean hasGrantedPolicy(android.content.ComponentName, int); method public boolean installCaCert(android.content.ComponentName, byte[]); @@ -14919,6 +14920,7 @@ package android.hardware.radio { public static class RadioManager.FmBandConfig extends android.hardware.radio.RadioManager.BandConfig { method public boolean getAf(); + method public boolean getEa(); method public boolean getRds(); method public boolean getStereo(); method public boolean getTa(); @@ -14930,6 +14932,7 @@ package android.hardware.radio { ctor public RadioManager.FmBandConfig.Builder(android.hardware.radio.RadioManager.FmBandConfig); method public android.hardware.radio.RadioManager.FmBandConfig build(); method public android.hardware.radio.RadioManager.FmBandConfig.Builder setAf(boolean); + method public android.hardware.radio.RadioManager.FmBandConfig.Builder setEa(boolean); method public android.hardware.radio.RadioManager.FmBandConfig.Builder setRds(boolean); method public android.hardware.radio.RadioManager.FmBandConfig.Builder setStereo(boolean); method public android.hardware.radio.RadioManager.FmBandConfig.Builder setTa(boolean); @@ -14937,6 +14940,7 @@ package android.hardware.radio { public static class RadioManager.FmBandDescriptor extends android.hardware.radio.RadioManager.BandDescriptor { method public boolean isAfSupported(); + method public boolean isEaSupported(); method public boolean isRdsSupported(); method public boolean isStereoSupported(); method public boolean isTaSupported(); @@ -14976,6 +14980,7 @@ package android.hardware.radio { method public boolean containsKey(java.lang.String); method public int describeContents(); method public android.graphics.Bitmap getBitmap(java.lang.String); + method public android.hardware.radio.RadioMetadata.Clock getClock(java.lang.String); method public int getInt(java.lang.String); method public java.lang.String getString(java.lang.String); method public java.util.Set<java.lang.String> keySet(); @@ -14985,6 +14990,7 @@ package android.hardware.radio { field public static final java.lang.String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM"; field public static final java.lang.String METADATA_KEY_ART = "android.hardware.radio.metadata.ART"; field public static final java.lang.String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST"; + field public static final java.lang.String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK"; field public static final java.lang.String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE"; field public static final java.lang.String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON"; field public static final java.lang.String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY"; @@ -15000,10 +15006,20 @@ package android.hardware.radio { ctor public RadioMetadata.Builder(android.hardware.radio.RadioMetadata); method public android.hardware.radio.RadioMetadata build(); method public android.hardware.radio.RadioMetadata.Builder putBitmap(java.lang.String, android.graphics.Bitmap); + method public android.hardware.radio.RadioMetadata.Builder putClock(java.lang.String, long, int); method public android.hardware.radio.RadioMetadata.Builder putInt(java.lang.String, int); method public android.hardware.radio.RadioMetadata.Builder putString(java.lang.String, java.lang.String); } + public static final class RadioMetadata.Clock implements android.os.Parcelable { + ctor public RadioMetadata.Clock(long, int); + method public int describeContents(); + method public int getTimezoneOffsetMinutes(); + method public long getUtcEpochSeconds(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioMetadata.Clock> CREATOR; + } + public abstract class RadioTuner { ctor public RadioTuner(); method public abstract int cancel(); @@ -15032,6 +15048,7 @@ package android.hardware.radio { method public void onAntennaState(boolean); method public void onConfigurationChanged(android.hardware.radio.RadioManager.BandConfig); method public void onControlChanged(boolean); + method public void onEmergencyAnnouncement(boolean); method public void onError(int); method public void onMetadataChanged(android.hardware.radio.RadioMetadata); method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo); diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 031bdbd1f222..eb7c71295617 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -28,24 +28,13 @@ import android.app.ActivityManagerNative; import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Context; -import android.content.IIntentReceiver; -import android.content.IIntentSender; -import android.content.Intent; -import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; -import android.content.pm.PackageInstaller; -import android.content.pm.PackageInstaller.SessionInfo; -import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.UserInfo; -import android.content.pm.VerificationParams; -import android.net.Uri; -import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -56,26 +45,13 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; -import libcore.io.IoUtils; - import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.SizedInputStream; -import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.TimeUnit; public final class Pm { private static final String TAG = "Pm"; @@ -106,7 +82,7 @@ public final class Pm { System.exit(exitCode); } - public int run(String[] args) throws IOException, RemoteException { + public int run(String[] args) throws RemoteException { boolean validCommand = false; if (args.length < 1) { return showUsage(); @@ -142,19 +118,19 @@ public final class Pm { } if ("install-create".equals(op)) { - return runInstallCreate(); + return runInstall(); } if ("install-write".equals(op)) { - return runInstallWrite(); + return runInstall(); } if ("install-commit".equals(op)) { - return runInstallCommit(); + return runInstall(); } if ("install-abandon".equals(op) || "install-destroy".equals(op)) { - return runInstallAbandon(); + return runInstall(); } if ("set-installer".equals(op)) { @@ -299,6 +275,10 @@ public final class Pm { return -1; } + private int runInstall() { + return runShellCommand("package", mArgs); + } + /** * Execute the list sub-command. * @@ -317,6 +297,10 @@ public final class Pm { return runShellCommand("package", mArgs); } + private int runUninstall() { + return runShellCommand("package", mArgs); + } + private int runPath() { int userId = UserHandle.USER_SYSTEM; String option = nextOption(); @@ -370,49 +354,6 @@ public final class Pm { } } - /** - * Converts a failure code into a string by using reflection to find a matching constant - * in PackageManager. - */ - private String installFailureToString(LocalPackageInstallObserver obs) { - final int result = obs.result; - Field[] fields = PackageManager.class.getFields(); - for (Field f: fields) { - if (f.getType() == int.class) { - int modifiers = f.getModifiers(); - // only look at public final static fields. - if (((modifiers & Modifier.FINAL) != 0) && - ((modifiers & Modifier.PUBLIC) != 0) && - ((modifiers & Modifier.STATIC) != 0)) { - String fieldName = f.getName(); - if (fieldName.startsWith("INSTALL_FAILED_") || - fieldName.startsWith("INSTALL_PARSE_FAILED_")) { - // get the int value and compare it to result. - try { - if (result == f.getInt(null)) { - StringBuilder sb = new StringBuilder(64); - sb.append(fieldName); - if (obs.extraPermission != null) { - sb.append(" perm="); - sb.append(obs.extraPermission); - } - if (obs.extraPackage != null) { - sb.append(" pkg=" + obs.extraPackage); - } - return sb.toString(); - } - } catch (IllegalAccessException e) { - // this shouldn't happen since we only look for public static fields. - } - } - } - } - } - - // couldn't find a matching constant? return the value - return Integer.toString(result); - } - // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined} private int runSetAppLink() { int userId = UserHandle.USER_SYSTEM; @@ -602,316 +543,6 @@ public final class Pm { } } - private int runInstall() { - int installFlags = 0; - int userId = UserHandle.USER_ALL; - String installerPackageName = null; - - String opt; - - String originatingUriString = null; - String referrer = null; - String abi = null; - - while ((opt=nextOption()) != null) { - if (opt.equals("-l")) { - installFlags |= PackageManager.INSTALL_FORWARD_LOCK; - } else if (opt.equals("-r")) { - installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; - } else if (opt.equals("-i")) { - installerPackageName = nextOptionData(); - if (installerPackageName == null) { - System.err.println("Error: no value specified for -i"); - return 1; - } - } else if (opt.equals("-t")) { - installFlags |= PackageManager.INSTALL_ALLOW_TEST; - } else if (opt.equals("-s")) { - // Override if -s option is specified. - installFlags |= PackageManager.INSTALL_EXTERNAL; - } else if (opt.equals("-f")) { - // Override if -s option is specified. - installFlags |= PackageManager.INSTALL_INTERNAL; - } else if (opt.equals("-d")) { - installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; - } else if (opt.equals("-g")) { - installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; - } else if (opt.equals("--originating-uri")) { - originatingUriString = nextOptionData(); - if (originatingUriString == null) { - System.err.println("Error: must supply argument for --originating-uri"); - return 1; - } - } else if (opt.equals("--referrer")) { - referrer = nextOptionData(); - if (referrer == null) { - System.err.println("Error: must supply argument for --referrer"); - return 1; - } - } else if (opt.equals("--abi")) { - abi = checkAbiArgument(nextOptionData()); - } else if (opt.equals("--user")) { - userId = Integer.parseInt(nextOptionData()); - } else { - System.err.println("Error: Unknown option: " + opt); - return 1; - } - } - - userId = translateUserId(userId, "runInstall"); - if (userId == UserHandle.USER_ALL) { - userId = UserHandle.USER_SYSTEM; - installFlags |= PackageManager.INSTALL_ALL_USERS; - } - - final Uri verificationURI; - final Uri originatingURI; - final Uri referrerURI; - - if (originatingUriString != null) { - originatingURI = Uri.parse(originatingUriString); - } else { - originatingURI = null; - } - - if (referrer != null) { - referrerURI = Uri.parse(referrer); - } else { - referrerURI = null; - } - - // Populate apkURI, must be present - final String apkFilePath = nextArg(); - System.err.println("\tpkg: " + apkFilePath); - if (apkFilePath == null) { - System.err.println("Error: no package specified"); - return 1; - } - - // Populate verificationURI, optionally present - final String verificationFilePath = nextArg(); - if (verificationFilePath != null) { - System.err.println("\tver: " + verificationFilePath); - verificationURI = Uri.fromFile(new File(verificationFilePath)); - } else { - verificationURI = null; - } - - LocalPackageInstallObserver obs = new LocalPackageInstallObserver(); - try { - VerificationParams verificationParams = new VerificationParams(verificationURI, - originatingURI, referrerURI, VerificationParams.NO_UID, null); - - mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, - installerPackageName, verificationParams, abi, userId); - - synchronized (obs) { - while (!obs.finished) { - try { - obs.wait(); - } catch (InterruptedException e) { - } - } - if (obs.result == PackageManager.INSTALL_SUCCEEDED) { - System.out.println("Success"); - return 0; - } else { - System.err.println("Failure [" - + installFailureToString(obs) - + "]"); - return 1; - } - } - } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(PM_NOT_RUNNING_ERR); - return 1; - } - } - - /** - * @param userId The user id to be translated. - * @param logContext Optional human readable text to provide some context in error log. - * @return Translated concrete user id. This will include USER_ALL. - */ - private int translateUserId(int userId, String logContext) { - return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), - userId, true, true, logContext, "pm command"); - } - - private int runInstallCreate() throws RemoteException { - int userId = UserHandle.USER_ALL; - String installerPackageName = null; - - final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); - - String opt; - while ((opt = nextOption()) != null) { - if (opt.equals("-l")) { - params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK; - } else if (opt.equals("-r")) { - params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; - } else if (opt.equals("-i")) { - installerPackageName = nextArg(); - if (installerPackageName == null) { - throw new IllegalArgumentException("Missing installer package"); - } - } else if (opt.equals("-t")) { - params.installFlags |= PackageManager.INSTALL_ALLOW_TEST; - } else if (opt.equals("-s")) { - params.installFlags |= PackageManager.INSTALL_EXTERNAL; - } else if (opt.equals("-f")) { - params.installFlags |= PackageManager.INSTALL_INTERNAL; - } else if (opt.equals("-d")) { - params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; - } else if (opt.equals("-g")) { - params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; - } else if (opt.equals("--originating-uri")) { - params.originatingUri = Uri.parse(nextOptionData()); - } else if (opt.equals("--referrer")) { - params.referrerUri = Uri.parse(nextOptionData()); - } else if (opt.equals("-p")) { - params.mode = SessionParams.MODE_INHERIT_EXISTING; - params.appPackageName = nextOptionData(); - if (params.appPackageName == null) { - throw new IllegalArgumentException("Missing inherit package name"); - } - } else if (opt.equals("-S")) { - params.setSize(Long.parseLong(nextOptionData())); - } else if (opt.equals("--abi")) { - params.abiOverride = checkAbiArgument(nextOptionData()); - } else if (opt.equals("--user")) { - userId = Integer.parseInt(nextOptionData()); - } else if (opt.equals("--install-location")) { - params.installLocation = Integer.parseInt(nextOptionData()); - } else if (opt.equals("--force-uuid")) { - params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID; - params.volumeUuid = nextOptionData(); - if ("internal".equals(params.volumeUuid)) { - params.volumeUuid = null; - } - } else { - throw new IllegalArgumentException("Unknown option " + opt); - } - } - - userId = translateUserId(userId, "runInstallCreate"); - if (userId == UserHandle.USER_ALL) { - userId = UserHandle.USER_SYSTEM; - params.installFlags |= PackageManager.INSTALL_ALL_USERS; - } - - final int sessionId = mInstaller.createSession(params, installerPackageName, userId); - - // NOTE: adb depends on parsing this string - System.out.println("Success: created install session [" + sessionId + "]"); - return 0; - } - - private int runInstallWrite() throws IOException, RemoteException { - long sizeBytes = -1; - - String opt; - while ((opt = nextOption()) != null) { - if (opt.equals("-S")) { - sizeBytes = Long.parseLong(nextOptionData()); - } else { - throw new IllegalArgumentException("Unknown option: " + opt); - } - } - - final int sessionId = Integer.parseInt(nextArg()); - final String splitName = nextArg(); - - String path = nextArg(); - if ("-".equals(path)) { - path = null; - } else if (path != null) { - final File file = new File(path); - if (file.isFile()) { - sizeBytes = file.length(); - } - } - - final SessionInfo info = mInstaller.getSessionInfo(sessionId); - - PackageInstaller.Session session = null; - InputStream in = null; - OutputStream out = null; - try { - session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); - - if (path != null) { - in = new FileInputStream(path); - } else { - in = new SizedInputStream(System.in, sizeBytes); - } - out = session.openWrite(splitName, 0, sizeBytes); - - int total = 0; - byte[] buffer = new byte[65536]; - int c; - while ((c = in.read(buffer)) != -1) { - total += c; - out.write(buffer, 0, c); - - if (info.sizeBytes > 0) { - final float fraction = ((float) c / (float) info.sizeBytes); - session.addProgress(fraction); - } - } - session.fsync(out); - - System.out.println("Success: streamed " + total + " bytes"); - return 0; - } finally { - IoUtils.closeQuietly(out); - IoUtils.closeQuietly(in); - IoUtils.closeQuietly(session); - } - } - - private int runInstallCommit() throws RemoteException { - final int sessionId = Integer.parseInt(nextArg()); - - PackageInstaller.Session session = null; - try { - session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); - - final LocalIntentReceiver receiver = new LocalIntentReceiver(); - session.commit(receiver.getIntentSender()); - - final Intent result = receiver.getResult(); - final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status == PackageInstaller.STATUS_SUCCESS) { - System.out.println("Success"); - return 0; - } else { - Log.e(TAG, "Failure details: " + result.getExtras()); - System.err.println("Failure [" - + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); - return 1; - } - } finally { - IoUtils.closeQuietly(session); - } - } - - private int runInstallAbandon() throws RemoteException { - final int sessionId = Integer.parseInt(nextArg()); - - PackageInstaller.Session session = null; - try { - session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); - session.abandon(); - System.out.println("Success"); - return 0; - } finally { - IoUtils.closeQuietly(session); - } - } - private int runSetInstaller() throws RemoteException { final String targetPackage = nextArg(); final String installerPackageName = nextArg(); @@ -1080,80 +711,6 @@ public final class Pm { } } - private int runUninstall() throws RemoteException { - int flags = 0; - int userId = UserHandle.USER_ALL; - - String opt; - while ((opt=nextOption()) != null) { - if (opt.equals("-k")) { - flags |= PackageManager.DELETE_KEEP_DATA; - } else if (opt.equals("--user")) { - String param = nextArg(); - if (isNumber(param)) { - userId = Integer.parseInt(param); - } else { - showUsage(); - System.err.println("Error: Invalid user: " + param); - return 1; - } - } else { - System.err.println("Error: Unknown option: " + opt); - return 1; - } - } - - String pkg = nextArg(); - if (pkg == null) { - System.err.println("Error: no package specified"); - return showUsage(); - } - - userId = translateUserId(userId, "runUninstall"); - if (userId == UserHandle.USER_ALL) { - userId = UserHandle.USER_SYSTEM; - flags |= PackageManager.DELETE_ALL_USERS; - } else { - PackageInfo info; - try { - info = mPm.getPackageInfo(pkg, 0, userId); - } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(PM_NOT_RUNNING_ERR); - return 1; - } - if (info == null) { - System.err.println("Failure - not installed for " + userId); - return 1; - } - final boolean isSystem = - (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - // If we are being asked to delete a system app for just one - // user set flag so it disables rather than reverting to system - // version of the app. - if (isSystem) { - flags |= PackageManager.DELETE_SYSTEM_APP; - } - } - - final LocalIntentReceiver receiver = new LocalIntentReceiver(); - mInstaller.uninstall(pkg, null /* callerPackageName */, flags, - receiver.getIntentSender(), userId); - - final Intent result = receiver.getResult(); - final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status == PackageInstaller.STATUS_SUCCESS) { - System.out.println("Success"); - return 0; - } else { - Log.e(TAG, "Failure details: " + result.getExtras()); - System.err.println("Failure [" - + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); - return 1; - } - } - static class ClearDataObserver extends IPackageDataObserver.Stub { boolean finished; boolean result; @@ -1499,54 +1056,6 @@ public final class Pm { return 1; } - private static String checkAbiArgument(String abi) { - if (TextUtils.isEmpty(abi)) { - throw new IllegalArgumentException("Missing ABI argument"); - } - - if ("-".equals(abi)) { - return abi; - } - - final String[] supportedAbis = Build.SUPPORTED_ABIS; - for (String supportedAbi : supportedAbis) { - if (supportedAbi.equals(abi)) { - return abi; - } - } - - throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); - } - - private static class LocalIntentReceiver { - private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>(); - - private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { - @Override - public int send(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - try { - mResult.offer(intent, 5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - return 0; - } - }; - - public IntentSender getIntentSender() { - return new IntentSender((IIntentSender) mLocalSender); - } - - public Intent getResult() { - try { - return mResult.take(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - private String nextOption() { if (mNextArg >= mArgs.length) { return null; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 7c3fcc30f6cf..2ac6cf93cc09 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -769,7 +769,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case RESIZE_STACK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); final int stackId = data.readInt(); - Rect r = Rect.CREATOR.createFromParcel(data); + final boolean hasRect = data.readInt() != 0; + Rect r = null; + if (hasRect) { + r = Rect.CREATOR.createFromParcel(data); + } final boolean allowResizeInDockedMode = data.readInt() == 1; resizeStack(stackId, r, allowResizeInDockedMode); reply.writeNoException(); @@ -3590,7 +3594,12 @@ class ActivityManagerProxy implements IActivityManager Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(stackId); - r.writeToParcel(data, 0); + if (r != null) { + data.writeInt(1); + r.writeToParcel(data, 0); + } else { + data.writeInt(0); + } data.writeInt(allowResizeInDockedMode ? 1 : 0); mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0); reply.readException(); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index c42ba65b0dc2..edafe59ca454 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -432,7 +432,7 @@ public final class PendingIntent implements Parcelable { * @param intents Array of Intents of the activities to be launched. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, - * or any of the flags as supported by + * {@link #FLAG_IMMUTABLE} or any of the flags as supported by * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts * of the intent that can be supplied when the actual send happens. * @@ -502,7 +502,7 @@ public final class PendingIntent implements Parcelable { * @param intent The Intent to be broadcast. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, - * or any of the flags as supported by + * {@link #FLAG_IMMUTABLE} or any of the flags as supported by * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts * of the intent that can be supplied when the actual send happens. * @@ -556,7 +556,7 @@ public final class PendingIntent implements Parcelable { * @param intent An Intent describing the service to be started. * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, - * or any of the flags as supported by + * {@link #FLAG_IMMUTABLE} or any of the flags as supported by * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts * of the intent that can be supplied when the actual send happens. * diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0fdf3d3693c3..d7ffcc4190f6 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3637,6 +3637,28 @@ public class DevicePolicyManager { } /** + * Called by a profile or device owner to get user restrictions set with + * {@link #addUserRestriction(ComponentName, String)}. + * <p> + * The target user may have more restrictions set by the system or other device owner / profile + * owner. To get all the user restrictions currently set, use + * {@link UserManager#getUserRestrictions()}. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + */ + public Bundle getUserRestrictions(@NonNull ComponentName admin) { + Bundle ret = null; + if (mService != null) { + try { + ret = mService.getUserRestrictions(admin); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return ret == null ? new Bundle() : ret; + } + + /** * Called by profile or device owners to hide or unhide packages. When a package is hidden it * is unavailable for use, but the data and actual package file remain. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index ccaa8cb82170..cfa58618138e 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -147,6 +147,7 @@ interface IDevicePolicyManager { ComponentName getRestrictionsProvider(int userHandle); void setUserRestriction(in ComponentName who, in String key, boolean enable); + Bundle getUserRestrictions(in ComponentName who); void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); void clearCrossProfileIntentFilters(in ComponentName admin); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 7ca39cb7c443..885255f81043 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -292,7 +292,7 @@ public class ActivityInfo extends ComponentInfo /** * @hide Bit in {@link #flags}: If set, this activity may be launched into an * owned ActivityContainer such as that within an ActivityView. If not set and - * this activity is launched into such a container a SecurityExcception will be + * this activity is launched into such a container a SecurityException will be * thrown. Set from the {@link android.R.attr#allowEmbedded} attribute. */ public static final int FLAG_ALLOW_EMBEDDED = 0x80000000; diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 32930a798201..14bb923767ed 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -457,14 +457,16 @@ public class RadioManager { private final boolean mRds; private final boolean mTa; private final boolean mAf; + private final boolean mEa; FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, - boolean stereo, boolean rds, boolean ta, boolean af) { + boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { super(region, type, lowerLimit, upperLimit, spacing); mStereo = stereo; mRds = rds; mTa = ta; mAf = af; + mEa = ea; } /** Stereo is supported @@ -492,6 +494,13 @@ public class RadioManager { return mAf; } + /** Emergency Announcement is supported + * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise. + */ + public boolean isEaSupported() { + return mEa; + } + /* Parcelable implementation */ private FmBandDescriptor(Parcel in) { super(in); @@ -499,6 +508,7 @@ public class RadioManager { mRds = in.readByte() == 1; mTa = in.readByte() == 1; mAf = in.readByte() == 1; + mEa = in.readByte() == 1; } public static final Parcelable.Creator<FmBandDescriptor> CREATOR @@ -519,6 +529,7 @@ public class RadioManager { dest.writeByte((byte) (mRds ? 1 : 0)); dest.writeByte((byte) (mTa ? 1 : 0)); dest.writeByte((byte) (mAf ? 1 : 0)); + dest.writeByte((byte) (mEa ? 1 : 0)); } @Override @@ -529,7 +540,8 @@ public class RadioManager { @Override public String toString() { return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo - + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + "]"; + + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + + ", mEa =" + mEa + "]"; } @Override @@ -540,6 +552,7 @@ public class RadioManager { result = prime * result + (mRds ? 1 : 0); result = prime * result + (mTa ? 1 : 0); result = prime * result + (mAf ? 1 : 0); + result = prime * result + (mEa ? 1 : 0); return result; } @@ -560,6 +573,8 @@ public class RadioManager { return false; if (mAf != other.isAfSupported()) return false; + if (mEa != other.isEaSupported()) + return false; return true; } } @@ -754,6 +769,7 @@ public class RadioManager { private final boolean mRds; private final boolean mTa; private final boolean mAf; + private final boolean mEa; FmBandConfig(FmBandDescriptor descriptor) { super((BandDescriptor)descriptor); @@ -761,15 +777,17 @@ public class RadioManager { mRds = descriptor.isRdsSupported(); mTa = descriptor.isTaSupported(); mAf = descriptor.isAfSupported(); + mEa = descriptor.isEaSupported(); } FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, - boolean stereo, boolean rds, boolean ta, boolean af) { + boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { super(region, type, lowerLimit, upperLimit, spacing); mStereo = stereo; mRds = rds; mTa = ta; mAf = af; + mEa = ea; } /** Get stereo enable state @@ -800,12 +818,21 @@ public class RadioManager { return mAf; } + /** + * Get Emergency announcement enable state + * @return the enable state. + */ + public boolean getEa() { + return mEa; + } + private FmBandConfig(Parcel in) { super(in); mStereo = in.readByte() == 1; mRds = in.readByte() == 1; mTa = in.readByte() == 1; mAf = in.readByte() == 1; + mEa = in.readByte() == 1; } public static final Parcelable.Creator<FmBandConfig> CREATOR @@ -826,6 +853,7 @@ public class RadioManager { dest.writeByte((byte) (mRds ? 1 : 0)); dest.writeByte((byte) (mTa ? 1 : 0)); dest.writeByte((byte) (mAf ? 1 : 0)); + dest.writeByte((byte) (mEa ? 1 : 0)); } @Override @@ -837,7 +865,7 @@ public class RadioManager { public String toString() { return "FmBandConfig [" + super.toString() + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa - + ", mAf=" + mAf + "]"; + + ", mAf=" + mAf + ", mEa =" + mEa + "]"; } @Override @@ -848,6 +876,7 @@ public class RadioManager { result = prime * result + (mRds ? 1 : 0); result = prime * result + (mTa ? 1 : 0); result = prime * result + (mAf ? 1 : 0); + result = prime * result + (mEa ? 1 : 0); return result; } @@ -868,6 +897,8 @@ public class RadioManager { return false; if (mAf != other.mAf) return false; + if (mEa != other.mEa) + return false; return true; } @@ -880,6 +911,7 @@ public class RadioManager { private boolean mRds; private boolean mTa; private boolean mAf; + private boolean mEa; /** * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} . @@ -893,6 +925,7 @@ public class RadioManager { mRds = descriptor.isRdsSupported(); mTa = descriptor.isTaSupported(); mAf = descriptor.isAfSupported(); + mEa = descriptor.isEaSupported(); } /** @@ -906,6 +939,7 @@ public class RadioManager { mRds = config.getRds(); mTa = config.getTa(); mAf = config.getAf(); + mEa = config.getEa(); } /** @@ -917,7 +951,7 @@ public class RadioManager { FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(), mDescriptor.getType(), mDescriptor.getLowerLimit(), mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), - mStereo, mRds, mTa, mAf); + mStereo, mRds, mTa, mAf, mEa); return config; } @@ -956,6 +990,15 @@ public class RadioManager { mAf = state; return this; } + + /** Set Emergency Announcement enable state + * @param state The new enable state. + * @return the same Builder instance. + */ + public Builder setEa(boolean state) { + mEa = state; + return this; + } }; } diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java index 8b1851b8397d..b7715da5ff61 100644 --- a/core/java/android/hardware/radio/RadioMetadata.java +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -95,11 +95,17 @@ public final class RadioMetadata implements Parcelable { */ public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART"; + /** + * The clock. + */ + public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK"; + private static final int METADATA_TYPE_INVALID = -1; private static final int METADATA_TYPE_INT = 0; private static final int METADATA_TYPE_TEXT = 1; private static final int METADATA_TYPE_BITMAP = 2; + private static final int METADATA_TYPE_CLOCK = 3; private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE; @@ -116,6 +122,7 @@ public final class RadioMetadata implements Parcelable { METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP); METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK); } // keep in sync with: system/media/radio/include/system/radio_metadata.h @@ -131,6 +138,7 @@ public final class RadioMetadata implements Parcelable { private static final int NATIVE_KEY_GENRE = 8; private static final int NATIVE_KEY_ICON = 9; private static final int NATIVE_KEY_ART = 10; + private static final int NATIVE_KEY_CLOCK = 11; private static final SparseArray<String> NATIVE_KEY_MAPPING; @@ -147,6 +155,59 @@ public final class RadioMetadata implements Parcelable { NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE); NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON); NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_CLOCK, METADATA_KEY_CLOCK); + } + + /** + * Provides a Clock that can be used to describe time as provided by the Radio. + * + * The clock is defined by the seconds since epoch at the UTC + 0 timezone + * and timezone offset from UTC + 0 represented in number of minutes. + * + * @hide + */ + @SystemApi + public static final class Clock implements Parcelable { + private final long mUtcEpochSeconds; + private final int mTimezoneOffsetMinutes; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mUtcEpochSeconds); + out.writeInt(mTimezoneOffsetMinutes); + } + + public static final Parcelable.Creator<Clock> CREATOR + = new Parcelable.Creator<Clock>() { + public Clock createFromParcel(Parcel in) { + return new Clock(in); + } + + public Clock[] newArray(int size) { + return new Clock[size]; + } + }; + + public Clock(long utcEpochSeconds, int timezoneOffsetMinutes) { + mUtcEpochSeconds = utcEpochSeconds; + mTimezoneOffsetMinutes = timezoneOffsetMinutes; + } + + private Clock(Parcel in) { + mUtcEpochSeconds = in.readLong(); + mTimezoneOffsetMinutes = in.readInt(); + } + + public long getUtcEpochSeconds() { + return mUtcEpochSeconds; + } + + public int getTimezoneOffsetMinutes() { + return mTimezoneOffsetMinutes; + } } private final Bundle mBundle; @@ -212,6 +273,17 @@ public final class RadioMetadata implements Parcelable { return bmp; } + public Clock getClock(String key) { + Clock clock = null; + try { + clock = mBundle.getParcelable(key); + } catch (Exception e) { + // ignore, value was not a clock. + Log.w(TAG, "Failed to retrieve a key as Clock.", e); + } + return clock; + } + @Override public int describeContents() { return 0; @@ -389,6 +461,27 @@ public final class RadioMetadata implements Parcelable { } /** + * Put a {@link RadioMetadata.Clock} into the meta data. Custom keys may be used, but if the + * METADATA_KEYs defined in this class are used they may only be one of the following: + * <ul> + * <li>{@link #MEADATA_KEY_CLOCK}</li> + * </ul> + * + * @param utcSecondsSinceEpoch Number of seconds since epoch for UTC + 0 timezone. + * @param timezoneOffsetInMinutes Offset of timezone from UTC + 0 in minutes. + * @return the same Builder instance. + */ + public Builder putClock(String key, long utcSecondsSinceEpoch, int timezoneOffsetMinutes) { + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a RadioMetadata.Clock."); + } + mBundle.putParcelable(key, new Clock(utcSecondsSinceEpoch, timezoneOffsetMinutes)); + return this; + } + + /** * Creates a {@link RadioMetadata} instance with the specified fields. * * @return a new {@link RadioMetadata} object @@ -446,4 +539,16 @@ public final class RadioMetadata implements Parcelable { return 0; } } + + int putClockFromNative(int nativeKey, long utcEpochSeconds, int timezoneOffsetInMinutes) { + Log.d(TAG, "putClockFromNative()"); + String key = getKeyFromNativeKey(nativeKey); + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_CLOCK) { + return -1; + } + mBundle.putParcelable(key, new RadioMetadata.Clock( + utcEpochSeconds, timezoneOffsetInMinutes)); + return 0; + } } diff --git a/core/java/android/hardware/radio/RadioModule.java b/core/java/android/hardware/radio/RadioModule.java index 15916ae60c59..fc7d0d2afbd4 100644 --- a/core/java/android/hardware/radio/RadioModule.java +++ b/core/java/android/hardware/radio/RadioModule.java @@ -89,6 +89,7 @@ public class RadioModule extends RadioTuner { static final int EVENT_METADATA = 4; static final int EVENT_TA = 5; static final int EVENT_AF_SWITCH = 6; + static final int EVENT_EA = 7; static final int EVENT_CONTROL = 100; static final int EVENT_SERVER_DIED = 101; @@ -170,6 +171,10 @@ public class RadioModule extends RadioTuner { callback.onTrafficAnnouncement(msg.arg2 == 1); } break; + case EVENT_EA: + if (callback != null) { + callback.onEmergencyAnnouncement(msg.arg2 == 1); + } case EVENT_CONTROL: if (callback != null) { callback.onControlChanged(msg.arg2 == 1); diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java index 376900a03444..5c82555663de 100644 --- a/core/java/android/hardware/radio/RadioTuner.java +++ b/core/java/android/hardware/radio/RadioTuner.java @@ -281,6 +281,10 @@ public abstract class RadioTuner { */ public void onTrafficAnnouncement(boolean active) {} /** + * onEmergencyAnnouncement() is called when an emergency annoucement starts and stops. + */ + public void onEmergencyAnnouncement(boolean active) {} + /** * onAntennaState() is called when the antenna is connected or disconnected. */ public void onAntennaState(boolean connected) {} diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index 73c2c804bdf1..cad482b6bab9 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -19,8 +19,11 @@ package android.os; import android.util.Slog; import com.android.internal.util.FastPrintWriter; +import java.io.BufferedInputStream; import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.InputStream; import java.io.PrintWriter; /** @@ -43,6 +46,7 @@ public abstract class ShellCommand { private FastPrintWriter mOutPrintWriter; private FastPrintWriter mErrPrintWriter; + private InputStream mInputStream; public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver) { @@ -111,6 +115,13 @@ public abstract class ShellCommand { return mErrPrintWriter; } + public InputStream getInputStream() { + if (mInputStream == null) { + mInputStream = new BufferedInputStream(new FileInputStream(mIn)); + } + return mInputStream; + } + /** * Return the next option on the command line -- that is an argument that * starts with '-'. If the next argument is not an option, null is returned. diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index b50cc79ad341..9178ec61f31d 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -19,6 +19,17 @@ package android.os; * @hide Only for use within the system server. */ public abstract class UserManagerInternal { + public interface UserRestrictionsListener { + /** + * Called when a user restriction changes. + * + * @param userId target user id + * @param newRestrictions new user restrictions + * @param prevRestrictions user restrictions that were previously set + */ + void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions); + } + /** * Lock that must be held when calling certain methods in this class. * @@ -60,4 +71,13 @@ public abstract class UserManagerInternal { */ public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId, Bundle baseRestrictions); + + /** Return a user restriction. */ + public abstract boolean getUserRestriction(int userId, String key); + + /** Adds a listener to user restriction changes. */ + public abstract void addUserRestrictionsListener(UserRestrictionsListener listener); + + /** Remove a {@link UserRestrictionsListener}. */ + public abstract void removeUserRestrictionsListener(UserRestrictionsListener listener); } diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java index c67535258c98..9bf344a0e2bc 100644 --- a/core/java/android/security/net/config/ApplicationConfig.java +++ b/core/java/android/security/net/config/ApplicationConfig.java @@ -48,7 +48,7 @@ public final class ApplicationConfig { */ public boolean hasPerDomainConfigs() { ensureInitialized(); - return mConfigs == null || !mConfigs.isEmpty(); + return mConfigs != null && !mConfigs.isEmpty(); } /** diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java index 915fbefb7041..0a4ce1126d05 100644 --- a/core/java/android/security/net/config/NetworkSecurityConfig.java +++ b/core/java/android/security/net/config/NetworkSecurityConfig.java @@ -17,6 +17,9 @@ package android.security.net.config; import android.util.ArraySet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -26,6 +29,12 @@ import javax.net.ssl.X509TrustManager; * @hide */ public final class NetworkSecurityConfig { + /** @hide */ + public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true; + /** @hide */ + public static final boolean DEFAULT_HSTS_ENFORCED = false; + public static final NetworkSecurityConfig DEFAULT = getDefaultBuilder().build(); + private final boolean mCleartextTrafficPermitted; private final boolean mHstsEnforced; private final PinSet mPins; @@ -35,7 +44,7 @@ public final class NetworkSecurityConfig { private X509TrustManager mTrustManager; private final Object mTrustManagerLock = new Object(); - public NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced, + private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced, PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) { mCleartextTrafficPermitted = cleartextTrafficPermitted; mHstsEnforced = hstsEnforced; @@ -83,4 +92,151 @@ public final class NetworkSecurityConfig { mAnchors = null; } } + + /** + * Return a {@link Builder} for the default {@code NetworkSecurityConfig}. + * + * <p> + * The default configuration has the following properties: + * <ol> + * <li>Cleartext traffic is permitted.</li> + * <li>HSTS is not enforced.</li> + * <li>No certificate pinning is used.</li> + * <li>The system and user added trusted certificate stores are trusted for connections.</li> + * </ol> + * + * @hide + */ + public static final Builder getDefaultBuilder() { + return new Builder() + .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED) + .setHstsEnforced(DEFAULT_HSTS_ENFORCED) + // System certificate store, does not bypass static pins. + .addCertificatesEntryRef( + new CertificatesEntryRef(SystemCertificateSource.getInstance(), false)) + // User certificate store, does not bypass static pins. + .addCertificatesEntryRef( + new CertificatesEntryRef(UserCertificateSource.getInstance(), false)); + } + + /** + * Builder for creating {@code NetworkSecurityConfig} objects. + * @hide + */ + public static final class Builder { + private List<CertificatesEntryRef> mCertificatesEntryRefs; + private PinSet mPinSet; + private boolean mCleartextTrafficPermitted = DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED; + private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED; + private boolean mCleartextTrafficPermittedSet = false; + private boolean mHstsEnforcedSet = false; + private Builder mParentBuilder; + + /** + * Sets the parent {@code Builder} for this {@code Builder}. + * The parent will be used to determine values not configured in this {@code Builder} + * in {@link Builder#build()}, recursively if needed. + */ + public Builder setParent(Builder parent) { + // Sanity check to avoid adding loops. + Builder current = parent; + while (current != null) { + if (current == this) { + throw new IllegalArgumentException("Loops are not allowed in Builder parents"); + } + current = current.getParent(); + } + mParentBuilder = parent; + return this; + } + + public Builder getParent() { + return mParentBuilder; + } + + public Builder setPinSet(PinSet pinSet) { + mPinSet = pinSet; + return this; + } + + private PinSet getEffectivePinSet() { + if (mPinSet != null) { + return mPinSet; + } + if (mParentBuilder != null) { + return mParentBuilder.getEffectivePinSet(); + } + return PinSet.EMPTY_PINSET; + } + + public Builder setCleartextTrafficPermitted(boolean cleartextTrafficPermitted) { + mCleartextTrafficPermitted = cleartextTrafficPermitted; + mCleartextTrafficPermittedSet = true; + return this; + } + + private boolean getEffectiveCleartextTrafficPermitted() { + if (mCleartextTrafficPermittedSet) { + return mCleartextTrafficPermitted; + } + if (mParentBuilder != null) { + return mParentBuilder.getEffectiveCleartextTrafficPermitted(); + } + return DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED; + } + + public Builder setHstsEnforced(boolean hstsEnforced) { + mHstsEnforced = hstsEnforced; + mHstsEnforcedSet = true; + return this; + } + + private boolean getEffectiveHstsEnforced() { + if (mHstsEnforcedSet) { + return mHstsEnforced; + } + if (mParentBuilder != null) { + return mParentBuilder.getEffectiveHstsEnforced(); + } + return DEFAULT_HSTS_ENFORCED; + } + + public Builder addCertificatesEntryRef(CertificatesEntryRef ref) { + if (mCertificatesEntryRefs == null) { + mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>(); + } + mCertificatesEntryRefs.add(ref); + return this; + } + + public Builder addCertificatesEntryRefs(Collection<? extends CertificatesEntryRef> refs) { + if (mCertificatesEntryRefs == null) { + mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>(); + } + mCertificatesEntryRefs.addAll(refs); + return this; + } + + private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() { + if (mCertificatesEntryRefs != null) { + return mCertificatesEntryRefs; + } + if (mParentBuilder != null) { + return mParentBuilder.getEffectiveCertificatesEntryRefs(); + } + return Collections.<CertificatesEntryRef>emptyList(); + } + + public boolean hasCertificateEntryRefs() { + return mCertificatesEntryRefs != null; + } + + public NetworkSecurityConfig build() { + boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted(); + boolean hstsEnforced = getEffectiveCleartextTrafficPermitted(); + PinSet pinSet = getEffectivePinSet(); + List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs(); + return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs); + } + } } diff --git a/core/java/android/security/net/config/PinSet.java b/core/java/android/security/net/config/PinSet.java index a9ee039fd01c..d3c975eb3101 100644 --- a/core/java/android/security/net/config/PinSet.java +++ b/core/java/android/security/net/config/PinSet.java @@ -17,10 +17,13 @@ package android.security.net.config; import android.util.ArraySet; +import java.util.Collections; import java.util.Set; /** @hide */ public final class PinSet { + public static final PinSet EMPTY_PINSET = + new PinSet(Collections.<Pin>emptySet(), Long.MAX_VALUE); public final long expirationTime; public final Set<Pin> pins; diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java index 640ebd9a9cf8..7649a977ff5c 100644 --- a/core/java/android/security/net/config/SystemCertificateSource.java +++ b/core/java/android/security/net/config/SystemCertificateSource.java @@ -36,18 +36,23 @@ import libcore.io.IoUtils; * @hide */ public class SystemCertificateSource implements CertificateSource { - private static Set<X509Certificate> sSystemCerts = null; - private static final Object sLock = new Object(); + private static final SystemCertificateSource INSTANCE = new SystemCertificateSource(); + private Set<X509Certificate> mSystemCerts = null; + private final Object mLock = new Object(); - public SystemCertificateSource() { + private SystemCertificateSource() { + } + + public static SystemCertificateSource getInstance() { + return INSTANCE; } @Override public Set<X509Certificate> getCertificates() { // TODO: loading all of these is wasteful, we should instead use a keystore style API. - synchronized (sLock) { - if (sSystemCerts != null) { - return sSystemCerts; + synchronized (mLock) { + if (mSystemCerts != null) { + return mSystemCerts; } CertificateFactory certFactory; try { @@ -83,14 +88,14 @@ public class SystemCertificateSource implements CertificateSource { IoUtils.closeQuietly(is); } } - sSystemCerts = systemCerts; - return sSystemCerts; + mSystemCerts = systemCerts; + return mSystemCerts; } } public void onCertificateStorageChange() { - synchronized (sLock) { - sSystemCerts = null; + synchronized (mLock) { + mSystemCerts = null; } } } diff --git a/core/java/android/security/net/config/UserCertificateSource.java b/core/java/android/security/net/config/UserCertificateSource.java index 77e2c88fe206..e9d5aa1e2f34 100644 --- a/core/java/android/security/net/config/UserCertificateSource.java +++ b/core/java/android/security/net/config/UserCertificateSource.java @@ -36,18 +36,23 @@ import libcore.io.IoUtils; * @hide */ public class UserCertificateSource implements CertificateSource { - private static Set<X509Certificate> sUserCerts = null; - private static final Object sLock = new Object(); + private static final UserCertificateSource INSTANCE = new UserCertificateSource(); + private Set<X509Certificate> mUserCerts = null; + private final Object mLock = new Object(); - public UserCertificateSource() { + private UserCertificateSource() { + } + + public static UserCertificateSource getInstance() { + return INSTANCE; } @Override public Set<X509Certificate> getCertificates() { // TODO: loading all of these is wasteful, we should instead use a keystore style API. - synchronized (sLock) { - if (sUserCerts != null) { - return sUserCerts; + synchronized (mLock) { + if (mUserCerts != null) { + return mUserCerts; } CertificateFactory certFactory; try { @@ -57,32 +62,31 @@ public class UserCertificateSource implements CertificateSource { } final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); final File userCaDir = new File(configDir, "cacerts-added"); - if (!userCaDir.isDirectory()) { - throw new AssertionError(userCaDir + " is not a directory"); - } - Set<X509Certificate> userCerts = new ArraySet<X509Certificate>(); - for (String caFile : userCaDir.list()) { - InputStream is = null; - try { - is = new BufferedInputStream( - new FileInputStream(new File(userCaDir, caFile))); - userCerts.add((X509Certificate) certFactory.generateCertificate(is)); - } catch (CertificateException | IOException e) { - // Don't rethrow to be consistent with conscrypt's cert loading code. - continue; - } finally { - IoUtils.closeQuietly(is); + // If the user hasn't added any certificates the directory may not exist. + if (userCaDir.isDirectory()) { + for (String caFile : userCaDir.list()) { + InputStream is = null; + try { + is = new BufferedInputStream( + new FileInputStream(new File(userCaDir, caFile))); + userCerts.add((X509Certificate) certFactory.generateCertificate(is)); + } catch (CertificateException | IOException e) { + // Don't rethrow to be consistent with conscrypt's cert loading code. + continue; + } finally { + IoUtils.closeQuietly(is); + } } } - sUserCerts = userCerts; - return sUserCerts; + mUserCerts = userCerts; + return mUserCerts; } } public void onCertificateStorageChange() { - synchronized (sLock) { - sUserCerts = null; + synchronized (mLock) { + mUserCerts = null; } } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index d598291f783a..d146e5e8ce27 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -270,7 +270,7 @@ public abstract class WallpaperService extends Service { @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, - Configuration newConfig) { + Configuration newConfig, Rect backDropRect) { Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, reportDraw ? 1 : 0, outsets); mCaller.sendMessage(msg); diff --git a/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl b/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl new file mode 100644 index 000000000000..622b9ddbf588 --- /dev/null +++ b/core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 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.view; + +import android.view.AppTransitionAnimationSpec; + +/** + * A cross-process future to fetch the specifications for app transitions. + * + * {@hide} + */ +interface IAppTransitionAnimationSpecsFuture { + AppTransitionAnimationSpec[] get(); +} diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index cc4bcb6e2d77..9e478c1d1e73 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -47,7 +47,7 @@ oneway interface IWindow { void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets, in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw, - in Configuration newConfig); + in Configuration newConfig, in Rect backDropFrame); void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index e8a1e32a0e63..7b5f5abe7535 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -28,6 +28,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IRemoteCallback; import android.view.IApplicationToken; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; @@ -140,6 +141,15 @@ interface IWindowManager void overridePendingAppTransitionMultiThumb(in AppTransitionAnimationSpec[] specs, IRemoteCallback startedCallback, IRemoteCallback finishedCallback, boolean scaleUp); void overridePendingAppTransitionInPlace(String packageName, int anim); + + /** + * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is + * used for recents, where generating the thumbnails of the specs takes a non-trivial amount of + * time, so we want to move that off the critical path for starting the new activity. + */ + void overridePendingAppTransitionMultiThumbFuture( + IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback, + boolean scaleUp); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, @@ -209,6 +219,11 @@ interface IWindowManager */ void cancelTaskWindowTransition(int taskId); + /** + * Cancels the thumbnail transitions for the given task. + */ + void cancelTaskThumbnailTransition(int taskId); + // These can only be called with the SET_ORIENTATION permission. /** * Update the current screen rotation based on the current state of @@ -317,4 +332,16 @@ interface IWindowManager * @return The frame statistics or null if the window does not exist. */ WindowContentFrameStats getWindowContentFrameStats(IBinder token); + + /** + * @return the dock side the current docked stack is at; must be one of the + * WindowManagerGlobal.DOCKED_* values + */ + int getDockedStackSide(); + + /** + * Sets whether we are currently in a drag resize operation where we are changing the docked + * stack size. + */ + void setDockedStackResizing(boolean resizing); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index dddea210ed83..514f88bc7f24 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -668,7 +668,7 @@ public class SurfaceView extends View { @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, - Configuration newConfig) { + Configuration newConfig, Rect backDropRect) { SurfaceView surfaceView = mSurfaceView.get(); if (surfaceView != null) { if (DEBUG) Log.v( diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index faeb35313174..ea42399a07eb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -287,6 +287,7 @@ public final class ViewRootImpl implements ViewParent, final Rect mPendingStableInsets = new Rect(); final Rect mPendingContentInsets = new Rect(); final Rect mPendingOutsets = new Rect(); + final Rect mPendingBackDropFrame = new Rect(); final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); @@ -1553,6 +1554,10 @@ public final class ViewRootImpl implements ViewParent, frame.height() < desiredWindowHeight && frame.height() != mHeight)); windowShouldResize |= mDragResizing; + // If the backdrop frame doesn't equal to a frame, we are starting a resize operation, so + // force it to be resized. + windowShouldResize |= !mPendingBackDropFrame.equals(mWinFrame); + // Determine whether to compute insets. // If there are no inset listeners remaining then we may still need to compute // insets in case the old insets were non-empty and must be reset. @@ -1733,7 +1738,7 @@ public final class ViewRootImpl implements ViewParent, & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0; if (mDragResizing != dragResizing) { if (dragResizing) { - startDragResizing(frame); + startDragResizing(mPendingBackDropFrame); } else { // We shouldn't come here, but if we come we should end the resize. endDragResizing(); @@ -3269,6 +3274,7 @@ public final class ViewRootImpl implements ViewParent, mPendingStableInsets.set((Rect) args.arg6); mPendingVisibleInsets.set((Rect) args.arg3); mPendingOutsets.set((Rect) args.arg7); + mPendingBackDropFrame.set((Rect) args.arg8); args.recycle(); @@ -3294,6 +3300,8 @@ public final class ViewRootImpl implements ViewParent, mWinFrame.top = t; mWinFrame.bottom = t + h; + mPendingBackDropFrame.set(mWinFrame); + if (mView != null) { forceLayout(mView); } @@ -5647,7 +5655,7 @@ public final class ViewRootImpl implements ViewParent, public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, - Configuration newConfig) { + Configuration newConfig, Rect backDropFrame) { if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString() + " contentInsets=" + contentInsets.toShortString() + " visibleInsets=" + visibleInsets.toShortString() @@ -5658,7 +5666,7 @@ public final class ViewRootImpl implements ViewParent, if (mDragResizing) { synchronized (mWindowCallbacks) { for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowSizeIsChanging(frame); + mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame); } } } @@ -5679,6 +5687,7 @@ public final class ViewRootImpl implements ViewParent, args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets; args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets; args.arg7 = sameProcessCall ? new Rect(outsets) : outsets; + args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame; msg.obj = args; mHandler.sendMessage(msg); } @@ -6677,11 +6686,11 @@ public final class ViewRootImpl implements ViewParent, @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, - Configuration newConfig) { + Configuration newConfig, Rect backDropFrame) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchResized(frame, overscanInsets, contentInsets, - visibleInsets, stableInsets, outsets, reportDraw, newConfig); + visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame); } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 92e473db556f..2d0435f4c7e3 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -50,6 +50,18 @@ import android.util.Log; * @see android.content.Context#WINDOW_SERVICE */ public interface WindowManager extends ViewManager { + + /** @hide */ + int DOCKED_INVALID = -1; + /** @hide */ + int DOCKED_LEFT = 1; + /** @hide */ + int DOCKED_TOP = 2; + /** @hide */ + int DOCKED_RIGHT = 3; + /** @hide */ + int DOCKED_BOTTOM = 4; + /** * Exception that is thrown when trying to add view whose * {@link LayoutParams} {@link LayoutParams#token} diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index ab99b9efae69..43d643e51c32 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -71,7 +71,7 @@ public final class WindowManagerGlobal { /** * The window is being resized by dragging one of the window corners, - * in this case the surface would be fullsreen-sized. The client should + * in this case the surface would be fullscreen-sized. The client should * render to the actual frame location (instead of (0,curScrollY)). */ public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8; diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java index c05e0d8f1256..8fb56d4757b6 100644 --- a/core/java/com/android/internal/os/SomeArgs.java +++ b/core/java/com/android/internal/os/SomeArgs.java @@ -47,6 +47,7 @@ public final class SomeArgs { public Object arg5; public Object arg6; public Object arg7; + public Object arg8; public int argi1; public int argi2; public int argi3; diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 07bfce727964..8699843eb83b 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -35,7 +35,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, - Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig) { + Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig, + Rect backDropFrame) { if (reportDraw) { try { mSession.finishDrawing(this); diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java index de542b1bc65f..de6e2286fde9 100644 --- a/core/java/com/android/internal/widget/NonClientDecorView.java +++ b/core/java/com/android/internal/widget/NonClientDecorView.java @@ -372,7 +372,7 @@ public class NonClientDecorView extends LinearLayout @Override public void onRequestDraw(boolean reportNextDraw) { if (mFrameRendererThread != null) { - mFrameRendererThread.onRequsetDraw(reportNextDraw); + mFrameRendererThread.onRequestDraw(reportNextDraw); } else if (reportNextDraw) { // If render thread is gone, just report immediately. if (isAttachedToWindow()) { @@ -517,7 +517,12 @@ public class NonClientDecorView extends LinearLayout public void run() { try { Looper.prepare(); - mChoreographer = Choreographer.getInstance(); + synchronized (this) { + mChoreographer = Choreographer.getInstance(); + + // Draw at least once. + mChoreographer.postFrameCallback(this); + } Looper.loop(); } finally { releaseRenderer(); @@ -580,7 +585,7 @@ public class NonClientDecorView extends LinearLayout } } - public void onRequsetDraw(boolean reportNextDraw) { + public void onRequestDraw(boolean reportNextDraw) { synchronized (this) { mReportNextDraw = reportNextDraw; mOldTargetRect.set(0, 0, 0, 0); diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index 2df3a4641aeb..3cdf640ae941 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -39,14 +39,14 @@ using namespace android; -class SkBitmapRegionDecoder { +class BitmapRegionDecoder { public: - SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) { + BitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) { fDecoder = decoder; fWidth = width; fHeight = height; } - ~SkBitmapRegionDecoder() { + ~BitmapRegionDecoder() { delete fDecoder; } @@ -91,7 +91,7 @@ static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream return nullObjectReturn("decoder->buildTileIndex returned false"); } - SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height); + BitmapRegionDecoder *bm = new BitmapRegionDecoder(decoder, width, height); return GraphicsJNI::createBitmapRegionDecoder(env, bm); } @@ -166,7 +166,7 @@ static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, */ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint start_x, jint start_y, jint width, jint height, jobject options) { - SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); jobject tileBitmap = NULL; SkImageDecoder *decoder = brd->getDecoder(); int sampleSize = 1; @@ -245,17 +245,17 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, } static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { - SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); return static_cast<jint>(brd->getHeight()); } static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { - SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); return static_cast<jint>(brd->getWidth()); } static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { - SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); delete brd; } diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 068517a12288..ed4401978f7b 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -439,7 +439,7 @@ int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap) return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID); } -jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) +jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap) { SkASSERT(bitmap != NULL); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index bcd834b0501b..90f8291fe846 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -12,7 +12,7 @@ #include <Canvas.h> #include <jni.h> -class SkBitmapRegionDecoder; +class BitmapRegionDecoder; class SkCanvas; namespace android { @@ -90,7 +90,7 @@ public: static jobject createRegion(JNIEnv* env, SkRegion* region); - static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + static jobject createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap); static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable); diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp index b9dd77af8c8a..ec6471e3dc83 100644 --- a/core/jni/android_hardware_Radio.cpp +++ b/core/jni/android_hardware_Radio.cpp @@ -93,6 +93,7 @@ static struct { jfieldID mRds; jfieldID mTa; jfieldID mAf; + jfieldID mEa; } gRadioFmBandConfigFields; static const char* const kRadioAmBandConfigClassPathName = @@ -117,6 +118,7 @@ static struct { jmethodID putIntFromNative; jmethodID putStringFromNative; jmethodID putBitmapFromNative; + jmethodID putClockFromNative; } gRadioMetadataMethods; static Mutex gLock; @@ -171,7 +173,8 @@ static jint convertBandDescriptorFromNative(JNIEnv *env, nBandconfig->band.fm.stereo, nBandconfig->band.fm.rds != RADIO_RDS_NONE, nBandconfig->band.fm.ta, - nBandconfig->band.fm.af); + nBandconfig->band.fm.af, + nBandconfig->band.fm.ea); } else if (nBandconfig->band.type == RADIO_BAND_AM) { *jBandDescriptor = env->NewObject(gRadioAmBandDescriptorClass, gRadioAmBandDescriptorCstor, nBandconfig->region, nBandconfig->band.type, @@ -205,7 +208,8 @@ static jint convertBandConfigFromNative(JNIEnv *env, nBandconfig->band.fm.stereo, nBandconfig->band.fm.rds != RADIO_RDS_NONE, nBandconfig->band.fm.ta, - nBandconfig->band.fm.af); + nBandconfig->band.fm.af, + nBandconfig->band.fm.ea); } else if (nBandconfig->band.type == RADIO_BAND_AM) { *jBandConfig = env->NewObject(gRadioAmBandConfigClass, gRadioAmBandConfigCstor, nBandconfig->region, nBandconfig->band.type, @@ -284,6 +288,18 @@ static jint convertMetadataFromNative(JNIEnv *env, } env->DeleteLocalRef(jData); } break; + case RADIO_METADATA_TYPE_CLOCK: { + ALOGV("%s RADIO_METADATA_TYPE_CLOCK %d", __FUNCTION__, key); + radio_metadata_clock_t *clock = (radio_metadata_clock_t *) value; + jStatus = + env->CallIntMethod(*jMetadata, + gRadioMetadataMethods.putClockFromNative, + key, (jint) clock->utc_seconds_since_epoch, + (jint) clock->timezone_offset_in_minutes); + if (jStatus == 0) { + jCount++; + } + } break; } } return jCount; @@ -351,6 +367,7 @@ static jint convertBandConfigToNative(JNIEnv *env, nBandconfig->region); nBandconfig->band.fm.ta = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mTa); nBandconfig->band.fm.af = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mAf); + nBandconfig->band.fm.ea = env->GetBooleanField(jBandConfig, gRadioFmBandConfigFields.mEa); } else if (env->IsInstanceOf(jBandConfig, gRadioAmBandConfigClass)) { nBandconfig->band.am.stereo = env->GetBooleanField(jBandConfig, gRadioAmBandConfigFields.mStereo); @@ -518,6 +535,7 @@ void JNIRadioCallback::onEvent(struct radio_event *event) break; case RADIO_EVENT_ANTENNA: case RADIO_EVENT_TA: + case RADIO_EVENT_EA: case RADIO_EVENT_CONTROL: jArg2 = event->on ? 1 : 0; break; @@ -878,7 +896,7 @@ int register_android_hardware_Radio(JNIEnv *env) jclass fmBandDescriptorClass = FindClassOrDie(env, kRadioFmBandDescriptorClassPathName); gRadioFmBandDescriptorClass = MakeGlobalRefOrDie(env, fmBandDescriptorClass); gRadioFmBandDescriptorCstor = GetMethodIDOrDie(env, fmBandDescriptorClass, "<init>", - "(IIIIIZZZZ)V"); + "(IIIIIZZZZZ)V"); jclass amBandDescriptorClass = FindClassOrDie(env, kRadioAmBandDescriptorClassPathName); gRadioAmBandDescriptorClass = MakeGlobalRefOrDie(env, amBandDescriptorClass); @@ -894,11 +912,13 @@ int register_android_hardware_Radio(JNIEnv *env) jclass fmBandConfigClass = FindClassOrDie(env, kRadioFmBandConfigClassPathName); gRadioFmBandConfigClass = MakeGlobalRefOrDie(env, fmBandConfigClass); gRadioFmBandConfigCstor = GetMethodIDOrDie(env, fmBandConfigClass, "<init>", - "(IIIIIZZZZ)V"); + "(IIIIIZZZZZ)V"); gRadioFmBandConfigFields.mStereo = GetFieldIDOrDie(env, fmBandConfigClass, "mStereo", "Z"); gRadioFmBandConfigFields.mRds = GetFieldIDOrDie(env, fmBandConfigClass, "mRds", "Z"); gRadioFmBandConfigFields.mTa = GetFieldIDOrDie(env, fmBandConfigClass, "mTa", "Z"); gRadioFmBandConfigFields.mAf = GetFieldIDOrDie(env, fmBandConfigClass, "mAf", "Z"); + gRadioFmBandConfigFields.mEa = + GetFieldIDOrDie(env, fmBandConfigClass, "mEa", "Z"); jclass amBandConfigClass = FindClassOrDie(env, kRadioAmBandConfigClassPathName); @@ -924,6 +944,9 @@ int register_android_hardware_Radio(JNIEnv *env) gRadioMetadataMethods.putBitmapFromNative = GetMethodIDOrDie(env, metadataClass, "putBitmapFromNative", "(I[B)I"); + gRadioMetadataMethods.putClockFromNative = GetMethodIDOrDie(env, metadataClass, + "putClockFromNative", + "(IJI)I"); RegisterMethodsOrDie(env, kRadioManagerClassPathName, gMethods, NELEM(gMethods)); diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index bfb0d103d7f2..28756f5d0384 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -45,8 +45,12 @@ <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> <dimen name="status_bar_edge_ignore">5dp</dimen> - <!-- Width of a divider bar used to resize docked stacks. --> - <dimen name="docked_stack_divider_thickness">24dp</dimen> + <!-- Width of the window of the divider bar used to resize docked stacks. --> + <dimen name="docked_stack_divider_thickness">48dp</dimen> + + <!-- How much the content in the divider is inset from the window bounds when resting. Used to + calculate the bounds of the stacks--> + <dimen name="docked_stack_divider_insets">18dp</dimen> <!-- Min width for a tablet device --> <dimen name="min_xlarge_screen_width">800dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5c65366b41b8..cda7faa1f09b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1481,6 +1481,7 @@ <java-symbol type="bool" name="config_supportAutoRotation" /> <java-symbol type="bool" name="target_honeycomb_needs_options_menu" /> <java-symbol type="dimen" name="docked_stack_divider_thickness" /> + <java-symbol type="dimen" name="docked_stack_divider_insets" /> <java-symbol type="dimen" name="navigation_bar_height" /> <java-symbol type="dimen" name="navigation_bar_height_landscape" /> <java-symbol type="dimen" name="navigation_bar_width" /> @@ -1724,7 +1725,6 @@ <java-symbol type="integer" name="config_undockedHdmiRotation" /> <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" /> <java-symbol type="layout" name="am_compat_mode_dialog" /> - <java-symbol type="layout" name="docked_stack_divider" /> <java-symbol type="layout" name="launch_warning" /> <java-symbol type="layout" name="safe_mode" /> <java-symbol type="layout" name="simple_list_item_2_single_choice" /> diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index b2044e1dcb4b..d05c66a8437c 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -144,7 +144,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mAlpha = alpha; if (mCurrDrawable != null) { if (mEnterAnimationEnd == 0) { - mCurrDrawable.mutate().setAlpha(alpha); + mCurrDrawable.setAlpha(alpha); } else { animate(false); } @@ -162,7 +162,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mDrawableContainerState.mDither != dither) { mDrawableContainerState.mDither = dither; if (mCurrDrawable != null) { - mCurrDrawable.mutate().setDither(mDrawableContainerState.mDither); + mCurrDrawable.setDither(mDrawableContainerState.mDither); } } } @@ -175,7 +175,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDrawableContainerState.mColorFilter = colorFilter; if (mCurrDrawable != null) { - mCurrDrawable.mutate().setColorFilter(colorFilter); + mCurrDrawable.setColorFilter(colorFilter); } } } @@ -188,7 +188,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDrawableContainerState.mTintList = tint; if (mCurrDrawable != null) { - mCurrDrawable.mutate().setTintList(tint); + mCurrDrawable.setTintList(tint); } } } @@ -201,7 +201,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDrawableContainerState.mTintMode = tintMode; if (mCurrDrawable != null) { - mCurrDrawable.mutate().setTintMode(tintMode); + mCurrDrawable.setTintMode(tintMode); } } } @@ -244,7 +244,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mDrawableContainerState.mAutoMirrored != mirrored) { mDrawableContainerState.mAutoMirrored = mirrored; if (mCurrDrawable != null) { - mCurrDrawable.mutate().setAutoMirrored(mDrawableContainerState.mAutoMirrored); + mCurrDrawable.setAutoMirrored(mDrawableContainerState.mAutoMirrored); } } } @@ -266,7 +266,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mCurrDrawable != null) { mCurrDrawable.jumpToCurrentState(); if (mHasAlpha) { - mCurrDrawable.mutate().setAlpha(mAlpha); + mCurrDrawable.setAlpha(mAlpha); } } if (mExitAnimationEnd != 0) { @@ -499,8 +499,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { * @param d The drawable to initialize. */ private void initializeDrawableForDisplay(Drawable d) { - d.mutate(); - if (mDrawableContainerState.mEnterFadeDuration <= 0 && mHasAlpha) { d.setAlpha(mAlpha); } @@ -540,13 +538,12 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mCurrDrawable != null) { if (mEnterAnimationEnd != 0) { if (mEnterAnimationEnd <= now) { - mCurrDrawable.mutate().setAlpha(mAlpha); + mCurrDrawable.setAlpha(mAlpha); mEnterAnimationEnd = 0; } else { int animAlpha = (int)((mEnterAnimationEnd-now)*255) / mDrawableContainerState.mEnterFadeDuration; - if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha); - mCurrDrawable.mutate().setAlpha(((255-animAlpha)*mAlpha)/255); + mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255); animating = true; } } @@ -563,8 +560,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } else { int animAlpha = (int)((mExitAnimationEnd-now)*255) / mDrawableContainerState.mExitFadeDuration; - if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha); - mLastDrawable.mutate().setAlpha((animAlpha*mAlpha)/255); + mLastDrawable.setAlpha((animAlpha*mAlpha)/255); animating = true; } } @@ -656,7 +652,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { int mChangingConfigurations; int mChildrenChangingConfigurations; - SparseArray<ConstantStateFuture> mDrawableFutures; + SparseArray<ConstantState> mDrawableFutures; Drawable[] mDrawables; int mNumChildren; @@ -757,7 +753,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDrawables = new Drawable[origDr.length]; mNumChildren = orig.mNumChildren; - final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures; + final SparseArray<ConstantState> origDf = orig.mDrawableFutures; if (origDf != null) { mDrawableFutures = origDf.clone(); } else { @@ -770,8 +766,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { final int N = mNumChildren; for (int i = 0; i < N; i++) { if (origDr[i] != null) { - if (origDr[i].getConstantState() != null) { - mDrawableFutures.put(i, new ConstantStateFuture(origDr[i])); + final ConstantState cs = origDr[i].getConstantState(); + if (cs != null) { + mDrawableFutures.put(i, cs); } else { mDrawables[i] = origDr[i]; } @@ -815,18 +812,26 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return mDrawables.length; } - private final void createAllFutures() { + private void createAllFutures() { if (mDrawableFutures != null) { final int futureCount = mDrawableFutures.size(); for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) { final int index = mDrawableFutures.keyAt(keyIndex); - mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this); + final ConstantState cs = mDrawableFutures.valueAt(keyIndex); + mDrawables[index] = prepareDrawable(cs.newDrawable(mSourceRes)); } mDrawableFutures = null; } } + private Drawable prepareDrawable(Drawable child) { + child.setLayoutDirection(mLayoutDirection); + child.setCallback(mOwner); + child = child.mutate(); + return child; + } + public final int getChildCount() { return mNumChildren; } @@ -851,7 +856,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mDrawableFutures != null) { final int keyIndex = mDrawableFutures.indexOfKey(index); if (keyIndex >= 0) { - final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this); + final ConstantState cs = mDrawableFutures.valueAt(keyIndex); + final Drawable prepared = prepareDrawable(cs.newDrawable(mSourceRes)); mDrawables[index] = prepared; mDrawableFutures.removeAt(keyIndex); if (mDrawableFutures.size() == 0) { @@ -938,7 +944,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return true; } } else { - final ConstantStateFuture future = mDrawableFutures.get(i); + final ConstantState future = mDrawableFutures.get(i); if (future != null && future.canApplyTheme()) { return true; } @@ -1173,49 +1179,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } return pixelCount; } - - /** - * Class capable of cloning a Drawable from another Drawable's - * ConstantState. - */ - private static class ConstantStateFuture { - private final ConstantState mConstantState; - - private ConstantStateFuture(Drawable source) { - mConstantState = source.getConstantState(); - } - - /** - * Obtains and prepares the Drawable represented by this future. - * - * @param state the container into which this future will be placed - * @return a prepared Drawable - */ - public Drawable get(DrawableContainerState state) { - final Drawable result; - if (state.mSourceRes == null) { - result = mConstantState.newDrawable(); - } else { - result = mConstantState.newDrawable(state.mSourceRes); - } - result.setLayoutDirection(state.mLayoutDirection); - result.setCallback(state.mOwner); - - if (state.mMutated) { - result.mutate(); - } - - return result; - } - - /** - * Whether the constant state wrapped by this future can apply a - * theme. - */ - public boolean canApplyTheme() { - return mConstantState.canApplyTheme(); - } - } } protected void setConstantState(DrawableContainerState state) { diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 1ede1052d21b..651b45334ee0 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -1601,8 +1601,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE; } + // Don't apply padding and insets for children that don't have + // an intrinsic dimension. final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth; - final int w = minWidth + insetL + insetR + padL + padR; + final int w = minWidth < 0 ? -1 : minWidth + insetL + insetR + padL + padR; if (w > width) { width = w; } @@ -1631,8 +1633,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { continue; } + // Don't apply padding and insets for children that don't have + // an intrinsic dimension. final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight; - final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB; + final int h = minHeight < 0 ? -1 : minHeight + r.mInsetT + r.mInsetB + padT + padB; if (h > height) { height = h; } diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp index bdb0c3d38f6f..8489acf6246f 100644 --- a/libs/androidfw/tests/AppAsLib_test.cpp +++ b/libs/androidfw/tests/AppAsLib_test.cpp @@ -16,7 +16,6 @@ #include <androidfw/ResourceTypes.h> -#include "data/basic/R.h" #include "data/appaslib/R.h" #include <gtest/gtest.h> @@ -25,29 +24,45 @@ using namespace android; namespace { -#include "data/basic/basic_arsc.h" +#include "data/appaslib/appaslib_arsc.h" +#include "data/appaslib/appaslib_lib_arsc.h" +// This tests the app resources loaded as app. TEST(AppAsLibTest, loadedAsApp) { ResTable table; - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len)); Res_value val; - ssize_t block = table.getResource(base::R::integer::number2, &val); + ssize_t block = table.getResource(appaslib::R::app::integer::number1, &val); ASSERT_GE(block, 0); ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(base::R::array::integerArray1, val.data); + ASSERT_EQ(appaslib::R::app::array::integerArray1, val.data); } +// This tests the app resources loaded as shared-lib. TEST(AppAsLibTest, loadedAsSharedLib) { ResTable table; // Load as shared library. - ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len, NULL, 0, -1, false, true)); + ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len, NULL, 0, -1, false, true)); Res_value val; - ssize_t block = table.getResource(appaslib::R::integer::number2, &val); + ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val); ASSERT_GE(block, 0); ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); - ASSERT_EQ(appaslib::R::array::integerArray1, val.data); + ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data); +} + +// This tests the shared-lib loaded with appAsLib as true. +TEST(AppAsLibTest, loadedSharedLib) { + ResTable table; + // Load shared library with appAsLib as true. + ASSERT_EQ(NO_ERROR, table.add(appaslib_lib_arsc, appaslib_lib_arsc_len, NULL, 0, -1, false, true)); + + Res_value val; + ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data); } } diff --git a/core/res/res/layout/docked_stack_divider.xml b/libs/androidfw/tests/data/appaslib/AndroidManifest.xml index aa6e68d57af9..e00045b0aa12 100644 --- a/core/res/res/layout/docked_stack_divider.xml +++ b/libs/androidfw/tests/data/appaslib/AndroidManifest.xml @@ -14,8 +14,8 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/docked_stack_divider_thickness" - android:layout_height="match_parent" - android:background="@android:color/black" - /> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.basic"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h index f89d4bfdd15e..3af921a7ba65 100644 --- a/libs/androidfw/tests/data/appaslib/R.h +++ b/libs/androidfw/tests/data/appaslib/R.h @@ -19,19 +19,33 @@ namespace appaslib { namespace R { - +namespace lib { namespace integer { enum { - number2 = 0x02040001, // default + number1 = 0x02020000, // default }; } namespace array { enum { - integerArray1 = 0x02060000, // default + integerArray1 = 0x02030000, // default + }; +} +} // namespace lib + +namespace app { +namespace integer { + enum { + number1 = 0x7f020000, // default }; } +namespace array { + enum { + integerArray1 = 0x7f030000, // default + }; +} +} // namespace app } // namespace R } // namespace appaslib diff --git a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h new file mode 100644 index 000000000000..be176ab5e63f --- /dev/null +++ b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h @@ -0,0 +1,68 @@ +unsigned char appaslib_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x04, 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, 0xdc, 0x02, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 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, 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, 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, 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, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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, + 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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, + 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, + 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x34, 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, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x7f, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x34, 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, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x03, 0x00, 0x00, 0x00 +}; +unsigned int appaslib_arsc_len = 772; diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h new file mode 100644 index 000000000000..099285a17aad --- /dev/null +++ b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h @@ -0,0 +1,68 @@ +unsigned char appaslib_lib_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x04, 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, 0xdc, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, + 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 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, 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, 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, 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, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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, + 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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, + 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, + 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x34, 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, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x34, 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, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x03, 0x00, 0x00, 0x00 +}; +unsigned int appaslib_lib_arsc_len = 772; diff --git a/libs/androidfw/tests/data/appaslib/build b/libs/androidfw/tests/data/appaslib/build new file mode 100755 index 000000000000..e4bd88b4032c --- /dev/null +++ b/libs/androidfw/tests/data/appaslib/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright (C) 2015 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. +# + +PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar + +aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc appaslib.arsc && \ +xxd -i appaslib.arsc > appaslib_arsc.h && \ +aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f --shared-lib && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc appaslib_lib.arsc && \ +xxd -i appaslib_lib.arsc > appaslib_lib_arsc.h \ + diff --git a/libs/androidfw/tests/data/appaslib/res/values/values.xml b/libs/androidfw/tests/data/appaslib/res/values/values.xml new file mode 100644 index 000000000000..39b99a6bfb81 --- /dev/null +++ b/libs/androidfw/tests/data/appaslib/res/values/values.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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> + <integer name="number1">@array/integerArray1</integer> + <integer-array name="integerArray1"> + <item>1</item> + <item>2</item> + <item>3</item> + </integer-array> +</resources> diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index cb638a476b14..772aa72b7fdc 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1398,6 +1398,7 @@ class DrawRenderNodeOp : public DrawBoundedOp { friend class RenderNode; // grant RenderNode access to info of child friend class DisplayList; // grant DisplayList access to info of child friend class DisplayListCanvas; + friend class TestUtils; public: DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple) : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr) diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp index 163f7cc4b1d0..68f80ea359cd 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/OpReorderer.cpp @@ -16,12 +16,14 @@ #include "OpReorderer.h" -#include "utils/PaintUtils.h" -#include "RenderNode.h" #include "LayerUpdateQueue.h" +#include "RenderNode.h" +#include "utils/FatVector.h" +#include "utils/PaintUtils.h" -#include "SkCanvas.h" -#include "utils/Trace.h" +#include <SkCanvas.h> +#include <utils/Trace.h> +#include <utils/TypeHelpers.h> namespace android { namespace uirenderer { @@ -359,7 +361,7 @@ void OpReorderer::onViewportInitialized() {} void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} void OpReorderer::deferNodePropsAndOps(RenderNode& node) { - if (node.applyViewProperties(mCanvasState)) { + if (node.applyViewProperties(mCanvasState, mAllocator)) { // not rejected so render if (node.getLayer()) { // HW layer @@ -375,6 +377,93 @@ void OpReorderer::deferNodePropsAndOps(RenderNode& node) { } } +typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair; + +template <typename V> +static void buildZSortedChildList(V* zTranslatedNodes, + const DisplayList& displayList, const DisplayList::Chunk& chunk) { + if (chunk.beginChildIndex == chunk.endChildIndex) return; + + for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) { + RenderNodeOp* childOp = displayList.getChildren()[i]; + RenderNode* child = childOp->renderNode; + float childZ = child->properties().getZ(); + + if (!MathUtils::isZero(childZ) && chunk.reorderChildren) { + zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp)); + childOp->skipInOrderDraw = true; + } else if (!child->properties().getProjectBackwards()) { + // regular, in order drawing DisplayList + childOp->skipInOrderDraw = false; + } + } + + // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order) + std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end()); +} + +template <typename V> +static size_t findNonNegativeIndex(const V& zTranslatedNodes) { + for (size_t i = 0; i < zTranslatedNodes.size(); i++) { + if (zTranslatedNodes[i].key >= 0.0f) return i; + } + return zTranslatedNodes.size(); +} + +template <typename V> +void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) { + const int size = zTranslatedNodes.size(); + if (size == 0 + || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f) + || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) { + // no 3d children to draw + return; + } + + /** + * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters + * with very similar Z heights to draw together. + * + * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are + * underneath both, and neither's shadow is drawn on top of the other. + */ + const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); + size_t drawIndex, shadowIndex, endIndex; + if (mode == ChildrenSelectMode::Negative) { + drawIndex = 0; + endIndex = nonNegativeIndex; + shadowIndex = endIndex; // draw no shadows + } else { + drawIndex = nonNegativeIndex; + endIndex = size; + shadowIndex = drawIndex; // potentially draw shadow for each pos Z child + } + + float lastCasterZ = 0.0f; + while (shadowIndex < endIndex || drawIndex < endIndex) { + if (shadowIndex < endIndex) { + const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value; + const float casterZ = zTranslatedNodes[shadowIndex].key; + // attempt to render the shadow if the caster about to be drawn is its caster, + // OR if its caster's Z value is similar to the previous potential caster + if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) { + deferShadow(*casterNodeOp); + + lastCasterZ = casterZ; // must do this even if current caster not casting a shadow + shadowIndex++; + continue; + } + } + + const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; + deferRenderNodeOp(*childOp); + drawIndex++; + } +} + +void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) { + // TODO +} /** * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods. * @@ -388,17 +477,20 @@ void OpReorderer::deferImpl(const DisplayList& displayList) { MAP_OPS(OP_RECEIVER) }; for (const DisplayList::Chunk& chunk : displayList.getChunks()) { + FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes; + buildZSortedChildList(&zTranslatedNodes, displayList, chunk); + + defer3dChildren(ChildrenSelectMode::Negative, zTranslatedNodes); for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { const RecordedOp* op = displayList.getOps()[opIndex]; receivers[op->opId](*this, *op); } + defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes); } } -void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) { - if (op.renderNode->nothingToDraw()) { - return; - } +void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) { + if (op.renderNode->nothingToDraw()) return; int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); // apply state from RecordedOp @@ -412,6 +504,12 @@ void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) { mCanvasState.restoreToCount(count); } +void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) { + if (!op.skipInOrderDraw) { + deferRenderNodeOp(op); + } +} + static batchid_t tessellatedBatchId(const SkPaint& paint) { return paint.getPathEffect() ? OpBatchType::AlphaMaskTexture diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h index 77be40292205..936b6ed9566e 100644 --- a/libs/hwui/OpReorderer.h +++ b/libs/hwui/OpReorderer.h @@ -182,6 +182,10 @@ public: virtual GLuint getTargetFbo() const override { return 0; } private: + enum class ChildrenSelectMode { + Negative, + Positive + }; void saveForLayer(uint32_t layerWidth, uint32_t layerHeight, const BeginLayerOp* beginLayerOp, RenderNode* renderNode); void restoreForLayer(); @@ -195,8 +199,15 @@ private: // should always be surrounded by a save/restore pair void deferNodePropsAndOps(RenderNode& node); + void deferShadow(const RenderNodeOp& casterOp); + void deferImpl(const DisplayList& displayList); + template <typename V> + void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes); + + void deferRenderNodeOp(const RenderNodeOp& op); + void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers); /** diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index 9ae868a9858c..04af8e36348a 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -93,6 +93,7 @@ struct RenderNodeOp : RecordedOp { : SUPER_PAINTLESS(RenderNodeOp) , renderNode(renderNode) {} RenderNode * renderNode; // not const, since drawing modifies it (somehow...) + bool skipInOrderDraw = false; }; struct BitmapOp : RecordedOp { diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 7c460b1bbc1a..e9885552369d 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -40,7 +40,7 @@ void RecordingCanvas::reset(int width, int height) { mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3()); - mDeferredBarrierType = kBarrier_InOrder; + mDeferredBarrierType = DeferredBarrierType::InOrder; mState.setDirtyClip(false); mRestoreSaveCount = -1; } @@ -432,17 +432,17 @@ size_t RecordingCanvas::addOp(RecordedOp* op) { // TODO: validate if "addDrawOp" quickrejection logic is useful before adding int insertIndex = mDisplayList->ops.size(); mDisplayList->ops.push_back(op); - if (mDeferredBarrierType != kBarrier_None) { + if (mDeferredBarrierType != DeferredBarrierType::None) { // op is first in new chunk mDisplayList->chunks.emplace_back(); DisplayList::Chunk& newChunk = mDisplayList->chunks.back(); newChunk.beginOpIndex = insertIndex; newChunk.endOpIndex = insertIndex + 1; - newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder); + newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder); int nextChildIndex = mDisplayList->children.size(); newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; - mDeferredBarrierType = kBarrier_None; + mDeferredBarrierType = DeferredBarrierType::None; } else { // standard case - append to existing chunk mDisplayList->chunks.back().endOpIndex = insertIndex + 1; diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 8a564758e7b1..fc84c98b76a5 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -39,6 +39,11 @@ class OpReceiver; struct RecordedOp; class RecordingCanvas: public Canvas, public CanvasStateClient { + enum class DeferredBarrierType { + None, + InOrder, + OutOfOrder, + }; public: RecordingCanvas(size_t width, size_t height); virtual ~RecordingCanvas(); @@ -49,7 +54,10 @@ public: // ---------------------------------------------------------------------------- // MISC HWUI OPERATIONS - TODO: CATEGORIZE // ---------------------------------------------------------------------------- - void insertReorderBarrier(bool enableReorder) {} + void insertReorderBarrier(bool enableReorder) { + mDeferredBarrierType = enableReorder + ? DeferredBarrierType::OutOfOrder : DeferredBarrierType::InOrder; + } void drawRenderNode(RenderNode* renderNode); // ---------------------------------------------------------------------------- @@ -176,11 +184,6 @@ public: virtual bool drawTextAbsolutePos() const override { return false; } private: - enum DeferredBarrierType { - kBarrier_None, - kBarrier_InOrder, - kBarrier_OutOfOrder, - }; void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint); @@ -290,7 +293,7 @@ private: CanvasState mState; std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy; ResourceCache& mResourceCache; - DeferredBarrierType mDeferredBarrierType = kBarrier_None; + DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None; DisplayList* mDisplayList = nullptr; bool mHighContrastText = false; SkAutoTUnref<SkDrawFilter> mDrawFilter; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 0601944905a8..15ca718481fe 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -282,9 +282,11 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { damageSelf(info); transformUpdateNeeded = true; #if HWUI_NEW_OPS - } else if (mLayer->viewportWidth != getWidth() || mLayer->viewportHeight != getHeight()) { - // TODO: allow it to grow larger - if (getWidth() > mLayer->texture.width || getHeight() > mLayer->texture.height) { + } else if (mLayer->viewportWidth != (uint32_t) getWidth() + || mLayer->viewportHeight != (uint32_t)getHeight()) { + // TODO: allow node's layer to grow larger + if ((uint32_t)getWidth() > mLayer->texture.width + || (uint32_t)getHeight() > mLayer->texture.height) { #else } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { @@ -304,7 +306,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { if (info.errorHandler) { std::ostringstream err; err << "Unable to create layer for " << getName(); - const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize; + const int maxTextureSize = Caches::getInstance().maxTextureSize; if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) { err << ", size " << getWidth() << "x" << getHeight() << " exceeds max size " << maxTextureSize; @@ -518,7 +520,7 @@ void RenderNode::decParentRefCount() { } } -bool RenderNode::applyViewProperties(CanvasState& canvasState) const { +bool RenderNode::applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const { const Outline& outline = properties().getOutline(); if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty()) @@ -542,6 +544,48 @@ bool RenderNode::applyViewProperties(CanvasState& canvasState) const { canvasState.concatMatrix(*properties().getTransformMatrix()); } } + + const bool isLayer = properties().effectiveLayerType() != LayerType::None; + int clipFlags = properties().getClippingFlags(); + if (properties().getAlpha() < 1) { + if (isLayer) { + clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer + } + if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) { + // simply scale rendering content's alpha + canvasState.scaleAlpha(properties().getAlpha()); + } else { + // savelayer needed to create an offscreen buffer + Rect layerBounds(0, 0, getWidth(), getHeight()); + if (clipFlags) { + properties().getClippingRectForFlags(clipFlags, &layerBounds); + clipFlags = 0; // all clipping done by savelayer + } + LOG_ALWAYS_FATAL("TODO: savelayer"); + } + + if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) { + // pretend alpha always causes savelayer to warn about + // performance problem affecting old versions + ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), getWidth(), getHeight()); + } + } + if (clipFlags) { + Rect clipRect; + properties().getClippingRectForFlags(clipFlags, &clipRect); + canvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, + SkRegion::kIntersect_Op); + } + + // TODO: support nesting round rect clips + if (mProperties.getRevealClip().willClip()) { + Rect bounds; + mProperties.getRevealClip().getBounds(&bounds); + canvasState.setClippingRoundRect(allocator, + bounds, mProperties.getRevealClip().getRadius()); + } else if (mProperties.getOutline().willClip()) { + canvasState.setClippingOutline(allocator, &(mProperties.getOutline())); + } return !canvasState.quickRejectConservative( 0, 0, properties().getWidth(), properties().getHeight()); } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 3500cb200a51..bae5ebe3f754 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -171,11 +171,11 @@ public: return mStagingProperties; } - uint32_t getWidth() { + int getWidth() const { return properties().getWidth(); } - uint32_t getHeight() { + int getHeight() const { return properties().getHeight(); } @@ -188,7 +188,7 @@ public: AnimatorManager& animators() { return mAnimatorManager; } // Returns false if the properties dictate the subtree contained in this RenderNode won't render - bool applyViewProperties(CanvasState& canvasState) const; + bool applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const; void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const; @@ -201,7 +201,6 @@ public: || properties().getScaleY() == 0; } - // Only call if RenderNode has DisplayList... const DisplayList* getDisplayList() const { return mDisplayList; } diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index abef806a6975..ca7789e6f19a 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,23 +16,24 @@ #ifndef RENDERNODEPROPERTIES_H #define RENDERNODEPROPERTIES_H -#include <algorithm> -#include <stddef.h> -#include <vector> -#include <cutils/compiler.h> -#include <androidfw/ResourceTypes.h> -#include <utils/Log.h> +#include "Caches.h" +#include "DeviceInfo.h" +#include "Rect.h" +#include "RevealClip.h" +#include "Outline.h" +#include "utils/MathUtils.h" #include <SkCamera.h> #include <SkMatrix.h> #include <SkRegion.h> #include <SkXfermode.h> -#include "Caches.h" -#include "Rect.h" -#include "RevealClip.h" -#include "Outline.h" -#include "utils/MathUtils.h" +#include <algorithm> +#include <stddef.h> +#include <vector> +#include <cutils/compiler.h> +#include <androidfw/ResourceTypes.h> +#include <utils/Log.h> class SkBitmap; class SkColorFilter; @@ -608,10 +609,11 @@ public: } bool promotedToLayer() const { - const int maxTextureSize = Caches::getInstance().maxTextureSize; + const DeviceInfo* deviceInfo = DeviceInfo::get(); + LOG_ALWAYS_FATAL_IF(!deviceInfo, "DeviceInfo uninitialized"); return mLayerProperties.mType == LayerType::None - && mPrimitiveFields.mWidth <= maxTextureSize - && mPrimitiveFields.mHeight <= maxTextureSize + && mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() + && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize() && (mComputedFields.mNeedLayerForFunctors || (!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 diff --git a/libs/hwui/unit_tests/DeviceInfoTests.cpp b/libs/hwui/unit_tests/DeviceInfoTests.cpp index c3c68aeb8818..17236bdf0bf7 100644 --- a/libs/hwui/unit_tests/DeviceInfoTests.cpp +++ b/libs/hwui/unit_tests/DeviceInfoTests.cpp @@ -14,8 +14,7 @@ * limitations under the License. */ - -#include "DeviceInfo.h" +#include <DeviceInfo.h> #include <gtest/gtest.h> @@ -23,10 +22,9 @@ using namespace android; using namespace android::uirenderer; TEST(DeviceInfo, basic) { - const DeviceInfo* di = DeviceInfo::get(); - EXPECT_EQ(nullptr, di) << "DeviceInfo was already initialized?"; + // can't assert state before init - another test may have initialized the singleton DeviceInfo::initialize(); - di = DeviceInfo::get(); + const DeviceInfo* di = DeviceInfo::get(); ASSERT_NE(nullptr, di) << "DeviceInfo initialization failed"; EXPECT_EQ(2048, di->maxTextureSize()) << "Max texture size didn't match"; } diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp index 9d625bc62696..ef205ec50ffc 100644 --- a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp +++ b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp @@ -32,7 +32,7 @@ TEST(LayerUpdateQueue, construct) { // sync node properties, so properties() reflects correct width and height static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) { sp<RenderNode> node = TestUtils::createNode(0, 0, width, height); - TestUtils::syncNodePropertiesAndDisplayList(node); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); return node; } diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp index 09b10c3af4f9..f67c24a1bce8 100644 --- a/libs/hwui/unit_tests/OpReordererTests.cpp +++ b/libs/hwui/unit_tests/OpReordererTests.cpp @@ -198,8 +198,7 @@ TEST(OpReorderer, renderNode) { canvas.restore(); }); - TestUtils::syncNodePropertiesAndDisplayList(child); - TestUtils::syncNodePropertiesAndDisplayList(parent); + TestUtils::syncHierarchyPropertiesAndDisplayList(parent); std::vector< sp<RenderNode> > nodes; nodes.push_back(parent.get()); @@ -225,7 +224,7 @@ TEST(OpReorderer, clipped) { SkBitmap bitmap = TestUtils::createSkBitmap(200, 200); canvas.drawBitmap(bitmap, 0, 0, nullptr); }); - TestUtils::syncNodePropertiesAndDisplayList(node); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); std::vector< sp<RenderNode> > nodes; nodes.push_back(node.get()); @@ -409,7 +408,7 @@ TEST(OpReorderer, hwLayerSimple) { OffscreenBuffer** bufferHandle = node->getLayerHandle(); *bufferHandle = (OffscreenBuffer*) 0x0124; - TestUtils::syncNodePropertiesAndDisplayList(node); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); std::vector< sp<RenderNode> > nodes; nodes.push_back(node.get()); @@ -483,7 +482,8 @@ public: } }; TEST(OpReorderer, hwLayerComplex) { - sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, [](RecordingCanvas& canvas) { + auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150, + [](RecordingCanvas& canvas) { SkPaint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); @@ -493,7 +493,8 @@ TEST(OpReorderer, hwLayerComplex) { *(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567; RenderNode* childPtr = child.get(); - sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) { + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, + [childPtr](RecordingCanvas& canvas) { SkPaint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, 200, 200, paint); @@ -506,8 +507,7 @@ TEST(OpReorderer, hwLayerComplex) { parent->setPropertyFieldsDirty(RenderNode::GENERIC); *(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123; - TestUtils::syncNodePropertiesAndDisplayList(child); - TestUtils::syncNodePropertiesAndDisplayList(parent); + TestUtils::syncHierarchyPropertiesAndDisplayList(parent); std::vector< sp<RenderNode> > nodes; nodes.push_back(parent.get()); @@ -527,5 +527,170 @@ TEST(OpReorderer, hwLayerComplex) { *(parent->getLayerHandle()) = nullptr; } + +class ZReorderTestRenderer : public TestRendererBase { +public: + void onRectOp(const RectOp& op, const BakedOpState& state) override { + int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel + EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order"; + } +}; +static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { + SkPaint paint; + paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel + canvas->drawRect(0, 0, 100, 100, paint); +} +static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, + [expectedDrawOrder](RecordingCanvas& canvas) { + drawOrderedRect(&canvas, expectedDrawOrder); + }); + node->mutateStagingProperties().setTranslationZ(z); + node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z); + canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership +} +TEST(OpReorderer, zReorder) { + auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, + [](RecordingCanvas& canvas) { + drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder + drawOrderedRect(&canvas, 1); + canvas.insertReorderBarrier(true); + drawOrderedNode(&canvas, 6, 2.0f); + drawOrderedRect(&canvas, 3); + drawOrderedNode(&canvas, 4, 0.0f); + drawOrderedRect(&canvas, 5); + drawOrderedNode(&canvas, 2, -2.0f); + drawOrderedNode(&canvas, 7, 2.0f); + canvas.insertReorderBarrier(false); + drawOrderedRect(&canvas, 8); + drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder + }); + TestUtils::syncHierarchyPropertiesAndDisplayList(parent); + + std::vector< sp<RenderNode> > nodes; + nodes.push_back(parent.get()); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, nodes); + + ZReorderTestRenderer renderer; + reorderer.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(10, renderer.getIndex()); +}; + + +class PropertyTestRenderer : public TestRendererBase { +public: + PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) + : mCallback(callback) {} + void onRectOp(const RectOp& op, const BakedOpState& state) override { + EXPECT_EQ(mIndex++, 0); + mCallback(op, state); + } + std::function<void(const RectOp&, const BakedOpState&)> mCallback; +}; + +static void testProperty( + std::function<int(RenderProperties&)> propSetupCallback, + std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { + auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RecordingCanvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); + node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties())); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + + std::vector< sp<RenderNode> > nodes; + nodes.push_back(node.get()); + + OpReorderer reorderer(sEmptyLayerUpdateQueue, + SkRect::MakeWH(100, 100), 200, 200, nodes); + + PropertyTestRenderer renderer(opValidateCallback); + reorderer.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; +} + +TEST(OpReorderer, renderPropOverlappingRenderingAlpha) { + testProperty([](RenderProperties& properties) { + properties.setAlpha(0.5f); + properties.setHasOverlappingRendering(false); + return RenderNode::ALPHA | RenderNode::GENERIC; + }, [](const RectOp& op, const BakedOpState& state) { + EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op"; + }); +} + +TEST(OpReorderer, renderPropClipping) { + testProperty([](RenderProperties& properties) { + properties.setClipToBounds(true); + properties.setClipBounds(Rect(10, 20, 300, 400)); + return RenderNode::GENERIC; + }, [](const RectOp& op, const BakedOpState& state) { + EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds) + << "Clip rect should be intersection of node bounds and clip bounds"; + }); +} + +TEST(OpReorderer, renderPropRevealClip) { + testProperty([](RenderProperties& properties) { + properties.mutableRevealClip().set(true, 50, 50, 25); + return RenderNode::GENERIC; + }, [](const RectOp& op, const BakedOpState& state) { + ASSERT_NE(nullptr, state.roundRectClipState); + EXPECT_TRUE(state.roundRectClipState->highPriority); + EXPECT_EQ(25, state.roundRectClipState->radius); + EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect); + }); +} + +TEST(OpReorderer, renderPropOutlineClip) { + testProperty([](RenderProperties& properties) { + properties.mutableOutline().setShouldClip(true); + properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f); + return RenderNode::GENERIC; + }, [](const RectOp& op, const BakedOpState& state) { + ASSERT_NE(nullptr, state.roundRectClipState); + EXPECT_FALSE(state.roundRectClipState->highPriority); + EXPECT_EQ(5, state.roundRectClipState->radius); + EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect); + }); +} + +TEST(OpReorderer, renderPropTransform) { + testProperty([](RenderProperties& properties) { + properties.setLeftTopRightBottom(10, 10, 110, 110); + + SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); + properties.setStaticMatrix(&staticMatrix); + + // ignored, since static overrides animation + SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); + properties.setAnimationMatrix(&animationMatrix); + + properties.setTranslationX(10); + properties.setTranslationY(20); + properties.setScaleX(0.5f); + properties.setScaleY(0.7f); + return RenderNode::GENERIC + | RenderNode::TRANSLATION_X | RenderNode::TRANSLATION_Y + | RenderNode::SCALE_X | RenderNode::SCALE_Y; + }, [](const RectOp& op, const BakedOpState& state) { + Matrix4 matrix; + matrix.loadTranslate(10, 10, 0); // left, top + matrix.scale(1.2f, 1.2f, 1); // static matrix + // ignore animation matrix, since static overrides it + + // translation xy + matrix.translate(10, 20); + + // scale xy (from default pivot - center) + matrix.translate(50, 50); + matrix.scale(0.5f, 0.7f, 1); + matrix.translate(-50, -50); + EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform) + << "Op draw matrix must match expected combination of transformation properties"; + }); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp index dcf1f648a9b5..83b37ab733a2 100644 --- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp +++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp @@ -25,7 +25,7 @@ namespace uirenderer { static void playbackOps(const DisplayList& displayList, std::function<void(const RecordedOp&)> opReceiver) { - for (const DisplayList::Chunk& chunk : displayList.getChunks()) { + for (auto& chunk : displayList.getChunks()) { for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { RecordedOp* op = displayList.getOps()[opIndex]; opReceiver(*op); @@ -224,5 +224,26 @@ TEST(RecordingCanvas, saveLayerRotateClipped) { EXPECT_EQ(3, count); } +TEST(RecordingCanvas, testReorderBarrier) { + auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 400, 400, SkPaint()); + canvas.insertReorderBarrier(true); + canvas.insertReorderBarrier(false); + canvas.insertReorderBarrier(false); + canvas.insertReorderBarrier(true); + canvas.drawRect(0, 0, 400, 400, SkPaint()); + canvas.insertReorderBarrier(false); + }); + + auto chunks = dl->getChunks(); + EXPECT_EQ(0u, chunks[0].beginOpIndex); + EXPECT_EQ(1u, chunks[0].endOpIndex); + EXPECT_FALSE(chunks[0].reorderChildren); + + EXPECT_EQ(1u, chunks[1].beginOpIndex); + EXPECT_EQ(2u, chunks[1].endOpIndex); + EXPECT_TRUE(chunks[1].reorderChildren); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h index 770f413352c2..28e0fd8f3464 100644 --- a/libs/hwui/unit_tests/TestUtils.h +++ b/libs/hwui/unit_tests/TestUtils.h @@ -16,6 +16,8 @@ #ifndef TEST_UTILS_H #define TEST_UTILS_H +#include <DeviceInfo.h> +#include <DisplayList.h> #include <Matrix.h> #include <Rect.h> #include <RenderNode.h> @@ -23,6 +25,12 @@ #include <renderthread/RenderThread.h> #include <Snapshot.h> +#if HWUI_NEW_OPS +#include <RecordedOp.h> +#else +#include <DisplayListOp.h> +#endif + #include <memory> namespace android { @@ -90,6 +98,10 @@ public: } static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) { + // if RenderNodes are being sync'd/used, device info will be needed, since + // DeviceInfo::maxTextureSize() affects layer property + DeviceInfo::initialize(); + sp<RenderNode> node = new RenderNode(); node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom); node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); @@ -112,9 +124,8 @@ public: return node; } - static void syncNodePropertiesAndDisplayList(sp<RenderNode>& node) { - node->syncProperties(); - node->syncDisplayList(); + static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) { + syncHierarchyPropertiesAndDisplayListImpl(node.get()); } typedef std::function<void(renderthread::RenderThread& thread)> RtCallback; @@ -142,6 +153,18 @@ public: TestTask task(rtCallback); renderthread::RenderThread::getInstance().queueAndWait(&task); } +private: + static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { + node->syncProperties(); + node->syncDisplayList(); + auto displayList = node->getDisplayList(); + if (displayList) { + for (auto&& childOp : displayList->getChildren()) { + syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode); + } + } + } + }; // class TestUtils } /* namespace uirenderer */ diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 57ee319f8ff3..3d78028a72f8 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -603,6 +603,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (DEBUG) Log.d(TAG, "received broadcast " + action); @@ -656,6 +657,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) { @@ -788,6 +790,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return new SimData(state, slotId, subId); } + @Override public String toString() { return "SimData{state=" + simState + ",slotId=" + slotId + ",subId=" + subId + "}"; } @@ -1693,5 +1696,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { for (int subId : mServiceStates.keySet()) { pw.println(" " + subId + "=" + mServiceStates.get(subId)); } + if (mFpm != null && mFpm.isHardwareDetected()) { + final int userId = ActivityManager.getCurrentUser(); + final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId); + pw.println(" Fingerprint state (user=" + userId + ")"); + pw.println(" allowed=" + isUnlockingWithFingerprintAllowed()); + pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId)); + pw.println(" authSinceBoot=" + + getStrongAuthTracker().hasUserAuthenticatedSinceBoot()); + pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId)); + pw.println(" possible=" + isUnlockWithFingerprintPossible(userId)); + pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); + pw.println(" timedout=" + hasFingerprintUnlockTimedOut(userId)); + pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); + } } } diff --git a/packages/SystemUI/res/drawable/docked_divider_handle.xml b/packages/SystemUI/res/drawable/docked_divider_handle.xml new file mode 100644 index 000000000000..ea32548b78cc --- /dev/null +++ b/packages/SystemUI/res/drawable/docked_divider_handle.xml @@ -0,0 +1,22 @@ +<!-- +Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2 (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"> + <size android:height="@dimen/docked_divider_handle_height" + android:width="@dimen/docked_divider_handle_width" /> + <corners radius="1dp" /> + <solid android:color="@color/docked_divider_handle" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml new file mode 100644 index 000000000000..5f42856b5929 --- /dev/null +++ b/packages/SystemUI/res/layout/docked_stack_divider.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> + +<com.android.systemui.stackdivider.DividerView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent"> + + <View + style="@style/DockedDividerBackground" + android:id="@+id/docked_divider_background" + android:background="@color/docked_divider_background"/> + + <ImageButton + style="@style/DockedDividerHandle" + android:id="@+id/docked_divider_handle" + android:layout_height="48dp" + android:layout_width="48dp" + android:background="@null" + android:src="@drawable/docked_divider_handle"/> + +</com.android.systemui.stackdivider.DividerView> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 6ef1adac3e02..456391d61216 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -22,4 +22,7 @@ <!-- Standard notification width + gravity --> <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen> <integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer> + + <dimen name="docked_divider_handle_width">2dp</dimen> + <dimen name="docked_divider_handle_height">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml index 89191984b9e8..dd396d921638 100644 --- a/packages/SystemUI/res/values-land/styles.xml +++ b/packages/SystemUI/res/values-land/styles.xml @@ -18,4 +18,15 @@ <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer"> <item name="android:layout_width">360dp</item> </style> + + <style name="DockedDividerBackground"> + <item name="android:layout_width">12dp</item> + <item name="android:layout_height">match_parent</item> + <item name="android:layout_gravity">center_horizontal</item> + </style> + + <style name="DockedDividerHandle"> + <item name="android:layout_gravity">center_vertical</item> + </style> + </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index f40f0d9aecdb..26a0577327e4 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -143,4 +143,7 @@ <color name="fab_ripple">#1fffffff</color><!-- 12% white --> <color name="fab_shape">#ff009688</color><!-- Teal 500 --> + + <color name="docked_divider_background">#ff000000</color> + <color name="docked_divider_handle">#ffffff</color> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c41352460c9e..73f63a968c29 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -577,4 +577,11 @@ <!-- TODO: Remove this --> <dimen name="qs_header_neg_padding">-8dp</dimen> + + <!-- How high we lift the divider when touching --> + <dimen name="docked_stack_divider_lift_elevation">6dp</dimen> + + <dimen name="docked_divider_handle_width">24dp</dimen> + <dimen name="docked_divider_handle_height">2dp</dimen> + </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 9e942f0b6904..4462a03e0ad3 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -304,4 +304,14 @@ <item name="android:background">@drawable/btn_borderless_rect</item> </style> + <style name="DockedDividerBackground"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">12dp</item> + <item name="android:layout_gravity">center_vertical</item> + </style> + + <style name="DockedDividerHandle"> + <item name="android:layout_gravity">center_horizontal</item> + </style> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 9551fb7bd535..949efc588dd9 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -27,6 +27,8 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.util.Log; +import com.android.systemui.stackdivider.Divider; + import java.util.HashMap; import java.util.Map; @@ -51,6 +53,7 @@ public class SystemUIApplication extends Application { com.android.systemui.power.PowerUI.class, com.android.systemui.media.RingtonePlayer.class, com.android.systemui.keyboard.KeyboardUI.class, + Divider.class }; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index ebfacac1b1fb..a429447e18e7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -21,6 +21,7 @@ package com.android.systemui.recents; */ public class Constants { + // TODO: Move into RecentsMetrics public static class Metrics { // DO NOT MODIFY THE ORDER OF THESE METRICS public static final int DismissSourceKeyboard = 0; @@ -28,6 +29,7 @@ public class Constants { public static final int DismissSourceHeaderButton = 2; } + // TODO: Move into RecentsDebugFlags public static class DebugFlags { public static class App { @@ -37,7 +39,7 @@ public class Constants { public static final boolean EnableTaskFiltering = false; // Enables dismiss-all public static final boolean EnableDismissAll = false; - // Enables fast-toggling + // Enables fast-toggling by just tapping on the recents button public static final boolean EnableFastToggleRecents = false; // Enables the thumbnail alpha on the front-most task public static final boolean EnableThumbnailAlphaOnFrontmost = false; @@ -57,14 +59,4 @@ public class Constants { public static final int SystemServicesProxyMockTaskCount = 100; } } - - public static class Values { - public static class App { - public static int AppWidgetHostId = 1024; - } - - public static class TaskStackView { - public static final int FilterStartDelay = 25; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index ab05bb08a0ff..6874247e5fa3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -32,19 +32,20 @@ import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.ViewStub; import android.view.ViewTreeObserver; - import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent; +import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent; +import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.activity.IterateRecentsEvent; -import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; @@ -57,7 +58,6 @@ import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent; import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent; import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent; -import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -104,9 +104,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Runnables to finish the Recents activity FinishRecentsRunnable mFinishLaunchHomeRunnable; - // Runnable to be executed after we paused ourselves - Runnable mAfterPauseRunnable; - // The trigger to automatically launch the current task DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() { @Override @@ -140,8 +137,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView try { startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT); } catch (Exception e) { - Console.logError(RecentsActivity.this, - getString(R.string.recents_launch_error_message, "Home")); + Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e); } } } @@ -348,7 +344,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Initialize the widget host (the host id is static and does not change) if (!Constants.DebugFlags.App.DisableSearchBar) { - mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId); + mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID); } mPackageMonitor = new RecentsPackageMonitor(); mPackageMonitor.register(this); @@ -426,9 +422,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override protected void onPause() { super.onPause(); - if (mAfterPauseRunnable != null) { - mRecentsView.post(mAfterPauseRunnable); - mAfterPauseRunnable = null; + + if (Constants.DebugFlags.App.EnableFastToggleRecents) { + // Stop the fast-toggle dozer + mIterateTrigger.stopDozing(); } if (Constants.DebugFlags.App.EnableFastToggleRecents) { @@ -513,12 +510,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) { // As we iterate to the next/previous task, cancel any current/lagging window // transition animations - RecentsConfiguration config = Recents.getConfiguration(); - RecentsActivityLaunchState launchState = config.getLaunchState(); - if (launchState.launchedToTaskId != -1) { - SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.cancelWindowTransition(launchState.launchedToTaskId); - } + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null)); // Focus the next task in the stack final boolean backward = event.isShiftPressed(); @@ -577,10 +569,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /**** RecentsView.RecentsViewCallbacks Implementation ****/ @Override - public void onTaskViewClicked() { - } - - @Override public void onTaskLaunchFailed() { // Return to Home dismissRecentsToHome(true); @@ -591,11 +579,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mFinishLaunchHomeRunnable.run(); } - @Override - public void runAfterPause(Runnable r) { - mAfterPauseRunnable = r; - } - /**** EventBus events ****/ public final void onBusEvent(ToggleRecentsEvent event) { @@ -664,6 +647,17 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); } + public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) { + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); + int launchToTaskId = launchState.launchedToTaskId; + if (launchToTaskId != -1 && + (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) { + SystemServicesProxy ssp = Recents.getSystemServices(); + ssp.cancelWindowTransition(launchState.launchedToTaskId); + ssp.cancelThumbnailTransition(getTaskId()); + } + } + public final void onBusEvent(AppWidgetProviderChangedEvent event) { refreshSearchWidgetView(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java index 9ee6f5b4d8e2..01ffd2a5afdc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -19,7 +19,6 @@ package com.android.systemui.recents; /** * The launch state of the RecentsActivity. * - * TODO: We will be refactoring this out RecentsConfiguration. * Current Constraints: * - needed in onStart() before onNewIntent() * - needs to be reset when Recents is hidden diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java index fc96c11e4124..573db98a889d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java @@ -26,6 +26,8 @@ import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEven /** Our special app widget host for the Search widget */ public class RecentsAppWidgetHost extends AppWidgetHost { + public static final int HOST_ID = 1024; + boolean mIsListening; public RecentsAppWidgetHost(Context context, int hostId) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 675ef2fe55ab..db65e003f32f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.recents; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; - import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ITaskStackListener; @@ -32,24 +30,24 @@ import android.graphics.RectF; import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; +import android.util.Log; import android.util.MutableBoolean; import android.view.AppTransitionAnimationSpec; import android.view.LayoutInflater; import android.view.View; - import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent; +import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.activity.IterateRecentsEvent; -import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; -import com.android.systemui.recents.misc.Console; +import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.ForegroundThread; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.model.RecentsTaskLoadPlan; @@ -57,14 +55,16 @@ import com.android.systemui.recents.model.RecentsTaskLoader; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskGrouping; import com.android.systemui.recents.model.TaskStack; -import com.android.systemui.recents.views.TaskStackView; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; +import com.android.systemui.recents.views.TaskStackView; import com.android.systemui.recents.views.TaskViewHeader; import com.android.systemui.recents.views.TaskViewTransform; import com.android.systemui.statusbar.phone.PhoneStatusBar; import java.util.ArrayList; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; + /** * An implementation of the Recents component for the current user. For secondary users, this can * be called remotely from the system user. @@ -75,7 +75,12 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub private final static String TAG = "RecentsImpl"; private final static boolean DEBUG = false; - private final static int sMinToggleDelay = 350; + // The minimum amount of time between each recents button press that we will handle + private final static int MIN_TOGGLE_DELAY_MS = 350; + // The duration within which the user releasing the alt tab (from when they pressed alt tab) + // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this + // duration, then we will toggle recents after this duration. + private final static int FAST_ALT_TAB_DELAY_MS = 225; public final static String RECENTS_PACKAGE = "com.android.systemui"; public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity"; @@ -156,6 +161,14 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub // Variables to keep track of if we need to start recents after binding boolean mTriggeredFromAltTab; long mLastToggleTime; + DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() { + @Override + public void run() { + // When this fires, then the user has not released alt-tab for at least + // FAST_ALT_TAB_DELAY_MS milliseconds + showRecents(mTriggeredFromAltTab); + } + }); Bitmap mThumbnailTransitionBitmapCache; Task mThumbnailTransitionBitmapCacheKey; @@ -164,7 +177,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub public RecentsImpl(Context context) { mContext = context; mHandler = new Handler(); - mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId); + mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID); Resources res = mContext.getResources(); LayoutInflater inflater = LayoutInflater.from(mContext); @@ -239,6 +252,29 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub @Override public void showRecents(boolean triggeredFromAltTab) { mTriggeredFromAltTab = triggeredFromAltTab; + if (mFastAltTabTrigger.hasTriggered()) { + // We are calling this from the doze trigger, so just fall through to show Recents + mFastAltTabTrigger.resetTrigger(); + } else if (mFastAltTabTrigger.isDozing()) { + // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab, + // otherwise, this is an additional tab (alt-tab*), which means that we should trigger + // immediately (fall through and disable the pending trigger) + // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started + // so we may actually additional signal to handle multiple quick tab cases. The + // severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS + // duration though + if (!triggeredFromAltTab) { + return; + } + mFastAltTabTrigger.stopDozing(); + } else { + // Otherwise, the doze trigger is not running, and if this is an alt tab, we should + // start the trigger and then wait for the hide (or for it to elapse) + if (triggeredFromAltTab) { + mFastAltTabTrigger.startDozing(); + return; + } + } try { // Check if the top task is in the home stack, and start the recents activity @@ -249,13 +285,24 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub startRecentsActivity(topTask, isTopTaskHome.value); } } catch (ActivityNotFoundException e) { - Console.logRawError("Failed to launch RecentAppsIntent", e); + Log.e(TAG, "Failed to launch RecentsActivity", e); } } @Override public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { if (mBootCompleted) { + if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) { + // The user has released alt-tab before the trigger has run, so just show the next + // task immediately + showNextTask(); + + // Cancel the fast alt-tab trigger + mFastAltTabTrigger.stopDozing(); + mFastAltTabTrigger.resetTrigger(); + return; + } + // Defer to the activity to handle hiding recents, if it handles it, then it must still // be visible EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab, @@ -265,6 +312,11 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub @Override public void toggleRecents() { + // Skip this toggle if we are already waiting to trigger recents via alt-tab + if (mFastAltTabTrigger.isDozing()) { + return; + } + mTriggeredFromAltTab = false; try { @@ -283,7 +335,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub // better than showing a janky screenshot). // NOTE: Ideally, the screenshot mechanism would take the window transform into // account - if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) { + if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) { return; } @@ -296,7 +348,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub // better than showing a janky screenshot). // NOTE: Ideally, the screenshot mechanism would take the window transform into // account - if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) { + if ((SystemClock.elapsedRealtime() - mLastToggleTime) < MIN_TOGGLE_DELAY_MS) { return; } @@ -305,7 +357,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub mLastToggleTime = SystemClock.elapsedRealtime(); } } catch (ActivityNotFoundException e) { - Console.logRawError("Failed to launch RecentAppsIntent", e); + Log.e(TAG, "Failed to launch RecentsActivity", e); } } @@ -335,10 +387,58 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub // Do nothing } + /** + * Transitions to the next recent task in the stack. + */ + public void showNextTask() { + SystemServicesProxy ssp = Recents.getSystemServices(); + RecentsTaskLoader loader = Recents.getTaskLoader(); + RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); + loader.preloadTasks(plan, true /* isTopTaskHome */); + TaskStack focusedStack = plan.getTaskStack(); + + // Return early if there are no tasks in the focused stack + if (focusedStack == null || focusedStack.getTaskCount() == 0) return; + + ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask(); + // Return early if there is no running task + if (runningTask == null) return; + // Return early if the running task is in the home stack (optimization) + if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return; + + // Find the task in the recents list + ArrayList<Task> tasks = focusedStack.getTasks(); + Task toTask = null; + ActivityOptions launchOpts = null; + int taskCount = tasks.size(); + for (int i = taskCount - 1; i >= 1; i--) { + Task task = tasks.get(i); + if (task.key.id == runningTask.id) { + toTask = tasks.get(i - 1); + launchOpts = ActivityOptions.makeCustomAnimation(mContext, + R.anim.recents_launch_prev_affiliated_task_target, + R.anim.recents_launch_prev_affiliated_task_source); + break; + } + } + + // Return early if there is no next task + if (toTask == null) { + ssp.startInPlaceAnimationOnFrontMostApplication( + ActivityOptions.makeCustomInPlaceAnimation(mContext, + R.anim.recents_launch_prev_affiliated_task_bounce)); + return; + } + + // Launch the task + ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts); + } + + /** + * Transitions to the next affiliated task. + */ public void showRelativeAffiliatedTask(boolean showNextTask) { - // Return early if there is no focused stack SystemServicesProxy ssp = Recents.getSystemServices(); - int focusedStackId = ssp.getFocusedStack(); RecentsTaskLoader loader = Recents.getTaskLoader(); RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext); loader.preloadTasks(plan, true /* isTopTaskHome */); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java new file mode 100644 index 000000000000..7604de1d05d0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.Task; + +/** + * This is sent when we want to cancel the enter-recents window animation for the launch task. + */ +public class CancelEnterRecentsWindowAnimationEvent extends EventBus.Event { + + // This is set for the task that is launching, which allows us to ensure that we are not + // cancelling the same task animation (it will just be overwritten instead) + public final Task launchTask; + + public CancelEnterRecentsWindowAnimationEvent(Task launchTask) { + this.launchTask = launchTask; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java deleted file mode 100644 index 28ac9d38967f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2014 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 com.android.systemui.recents.misc; - - -import android.content.ComponentCallbacks2; -import android.content.Context; -import android.util.Log; -import android.view.MotionEvent; -import android.widget.Toast; - -import java.util.HashMap; -import java.util.Map; - - -public class Console { - // Timer - public static final Map<Object, Long> mTimeLogs = new HashMap<Object, Long>(); - - // Colors - public static final String AnsiReset = "\u001B[0m"; - public static final String AnsiBlack = "\u001B[30m"; - public static final String AnsiRed = "\u001B[31m"; // SystemUIHandshake - public static final String AnsiGreen = "\u001B[32m"; // MeasureAndLayout - public static final String AnsiYellow = "\u001B[33m"; // SynchronizeViewsWithModel - public static final String AnsiBlue = "\u001B[34m"; // TouchEvents, Search - public static final String AnsiPurple = "\u001B[35m"; // Draw - public static final String AnsiCyan = "\u001B[36m"; // ClickEvents - public static final String AnsiWhite = "\u001B[37m"; - - // Console enabled state - public static boolean Enabled = false; - - /** Logs a key */ - public static void log(String key) { - log(true, key, "", AnsiReset); - } - - /** Logs a conditioned key */ - public static void log(boolean condition, String key) { - if (condition) { - log(condition, key, "", AnsiReset); - } - } - - /** Logs a key in a specific color */ - public static void log(boolean condition, String key, Object data) { - if (condition) { - log(condition, key, data, AnsiReset); - } - } - - /** Logs a key with data in a specific color */ - public static void log(boolean condition, String key, Object data, String color) { - if (condition) { - System.out.println(color + key + AnsiReset + " " + data.toString()); - } - } - - /** Logs an error */ - public static void logError(Context context, String msg) { - Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); - Log.e("Recents", msg); - } - - /** Logs a raw error */ - public static void logRawError(String msg, Exception e) { - Log.e("Recents", msg, e); - } - - /** Logs a divider bar */ - public static void logDivider(boolean condition) { - if (condition) { - System.out.println("==== [" + System.currentTimeMillis() + - "] ============================================================"); - } - } - - /** Starts a time trace */ - public static void logStartTracingTime(boolean condition, String key) { - if (condition) { - long curTime = System.currentTimeMillis(); - mTimeLogs.put(key, curTime); - Console.log(condition, "[Recents|" + key + "]", - "started @ " + curTime); - } - } - - /** Continues a time trace */ - public static void logTraceTime(boolean condition, String key, String desc) { - if (condition) { - long timeDiff = System.currentTimeMillis() - mTimeLogs.get(key); - Console.log(condition, "[Recents|" + key + "|" + desc + "]", - "+" + timeDiff + "ms"); - } - } - - /** Logs a stack trace */ - public static void logStackTrace() { - logStackTrace("", 99); - } - - /** Logs a stack trace to a certain depth */ - public static void logStackTrace(int depth) { - logStackTrace("", depth); - } - - /** Logs a stack trace to a certain depth with a key */ - public static void logStackTrace(String key, int depth) { - int offset = 0; - StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); - String tinyStackTrace = ""; - // Skip over the known stack trace classes - for (int i = 0; i < callStack.length; i++) { - StackTraceElement el = callStack[i]; - String className = el.getClassName(); - if (className.indexOf("dalvik.system.VMStack") == -1 && - className.indexOf("java.lang.Thread") == -1 && - className.indexOf("recents.Console") == -1) { - break; - } else { - offset++; - } - } - // Build the pretty stack trace - int start = Math.min(offset + depth, callStack.length); - int end = offset; - String indent = ""; - for (int i = start - 1; i >= end; i--) { - StackTraceElement el = callStack[i]; - tinyStackTrace += indent + " -> " + el.getClassName() + - "[" + el.getLineNumber() + "]." + el.getMethodName(); - if (i > end) { - tinyStackTrace += "\n"; - indent += " "; - } - } - log(true, key, tinyStackTrace, AnsiRed); - } - - - /** Returns the stringified MotionEvent action */ - public static String motionEventActionToString(int action) { - switch (action) { - case MotionEvent.ACTION_DOWN: - return "Down"; - case MotionEvent.ACTION_UP: - return "Up"; - case MotionEvent.ACTION_MOVE: - return "Move"; - case MotionEvent.ACTION_CANCEL: - return "Cancel"; - case MotionEvent.ACTION_POINTER_DOWN: - return "Pointer Down"; - case MotionEvent.ACTION_POINTER_UP: - return "Pointer Up"; - default: - return "" + action; - } - } - - public static String trimMemoryLevelToString(int level) { - switch (level) { - case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN: - return "UI Hidden"; - case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE: - return "Running Moderate"; - case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND: - return "Background"; - case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: - return "Running Low"; - case ComponentCallbacks2.TRIM_MEMORY_MODERATE: - return "Moderate"; - case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: - return "Critical"; - case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: - return "Complete"; - default: - return "" + level; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java index 336d2db443f9..dc8f5476f254 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java @@ -29,44 +29,53 @@ public class DozeTrigger { boolean mIsDozing; boolean mHasTriggered; int mDozeDurationMilliseconds; - Runnable mSleepRunnable; + Runnable mOnSleepRunnable; // Sleep-runnable Runnable mDozeRunnable = new Runnable() { @Override public void run() { - mSleepRunnable.run(); mIsDozing = false; mHasTriggered = true; + mOnSleepRunnable.run(); } }; - public DozeTrigger(int dozeDurationMilliseconds, Runnable sleepRunnable) { + public DozeTrigger(int dozeDurationMilliseconds, Runnable onSleepRunnable) { mHandler = new Handler(); mDozeDurationMilliseconds = dozeDurationMilliseconds; - mSleepRunnable = sleepRunnable; + mOnSleepRunnable = onSleepRunnable; } - /** Starts dozing. This also resets the trigger flag. */ + /** + * Starts dozing and queues the onSleepRunnable to be called. This also resets the trigger flag. + */ public void startDozing() { forcePoke(); mHasTriggered = false; } - /** Stops dozing. */ + /** + * Stops dozing and prevents the onSleepRunnable from being called. + */ public void stopDozing() { mHandler.removeCallbacks(mDozeRunnable); mIsDozing = false; } - /** Poke this dozer to wake it up for a little bit, if it is dozing. */ + /** + * Poke this dozer to wake it up if it is dozing, delaying the onSleepRunnable from being + * called for a for the doze duration. + */ public void poke() { if (mIsDozing) { forcePoke(); } } - /** Poke this dozer to wake it up for a little bit. */ + /** + * Poke this dozer to wake it up even if it is not currently dozing. + */ void forcePoke() { mHandler.removeCallbacks(mDozeRunnable); mHandler.postDelayed(mDozeRunnable, mDozeDurationMilliseconds); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java index c87a901341d1..401b4487d621 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java @@ -19,6 +19,7 @@ package com.android.systemui.recents.misc; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.util.Log; import java.util.ArrayList; @@ -28,6 +29,8 @@ import java.util.ArrayList; */ public class ReferenceCountedTrigger { + private static final String TAG = "ReferenceCountedTrigger"; + Context mContext; int mCount; ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>(); @@ -94,8 +97,7 @@ public class ReferenceCountedTrigger { if (mErrorRunnable != null) { mErrorRunnable.run(); } else { - new Throwable("Invalid ref count").printStackTrace(); - Console.logError(mContext, "Invalid ref count"); + Log.e(TAG, "Invalid ref count"); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 4ee0357123aa..0432ac99de2f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -363,6 +363,19 @@ public class SystemServicesProxy { } } + /** + * Cancels the current thumbnail transtion to/from Recents for the given task id. + */ + public void cancelThumbnailTransition(int taskId) { + if (mWm == null) return; + + try { + WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + /** Returns the top task thumbnail for the given task id */ public Bitmap getTaskThumbnail(int taskId) { if (mAm == null) return null; @@ -775,8 +788,7 @@ public class SystemServicesProxy { taskId, INVALID_STACK_ID, options == null ? null : options.toBundle()); return true; } catch (Exception e) { - Console.logError(context, - context.getString(R.string.recents_launch_error_message, taskName)); + Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); } } return false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 495c8fd2bc5d..6734012c4e3d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -17,14 +17,12 @@ package com.android.systemui.recents.model; import android.animation.ObjectAnimator; -import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; -import android.util.Log; import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 1f80460714ba..5f94fa76f380 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -17,6 +17,7 @@ package com.android.systemui.recents.views; import android.app.ActivityOptions; +import android.app.ActivityOptions.OnAnimationStartedListener; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -30,6 +31,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.AppTransitionAnimationSpec; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -38,6 +40,8 @@ import android.view.WindowManagerGlobal; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; + +import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.Constants; @@ -47,6 +51,7 @@ import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsAppWidgetHostView; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; import com.android.systemui.recents.events.ui.DismissTaskViewEvent; @@ -60,8 +65,8 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; import java.util.List; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; /** @@ -78,10 +83,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** The RecentsView callbacks */ public interface RecentsViewCallbacks { - public void onTaskViewClicked(); public void onTaskLaunchFailed(); public void onAllTaskViewsDismissed(); - public void runAfterPause(Runnable r); } LayoutInflater mInflater; @@ -104,6 +107,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV Rect mSystemInsets = new Rect(); + + @GuardedBy("this") + List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs; + public RecentsView(Context context) { super(context); } @@ -240,6 +247,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } ctx.postAnimationTrigger.decrement(); + // If we are going home, cancel the previous task's window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null)); + // Notify of the exit animation EventBus.getDefault().send(new DismissRecentsToHomeAnimationStarted()); } @@ -412,56 +422,37 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } - private void postDrawHeaderThumbnailTransitionRunnable(final TaskStackView view, - final TaskView clickedView, final int offsetX, final int offsetY, - final float stackScroll, - final ActivityOptions.OnAnimationStartedListener animStartedListener, - final int destinationStack) { - Runnable r = new Runnable() { - @Override - public void run() { - overrideDrawHeaderThumbnailTransition(view, clickedView, offsetX, offsetY, - stackScroll, animStartedListener, destinationStack); - - } - }; - - mCb.runAfterPause(r); - } - - private void overrideDrawHeaderThumbnailTransition(TaskStackView stackView, - TaskView clickedTask, int offsetX, int offsetY, float stackScroll, - final ActivityOptions.OnAnimationStartedListener animStartedListener, - int destinationStack) { - List<AppTransitionAnimationSpec> specs = getAppTransitionAnimationSpecs(stackView, - clickedTask, offsetX, offsetY, stackScroll, destinationStack); - if (specs == null) { - return; - } - - IRemoteCallback.Stub callback = new IRemoteCallback.Stub() { + private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final TaskStackView stackView, + final TaskView clickedTask, final int offsetX, final int offsetY, + final float stackScroll, final int destinationStack) { + return new IAppTransitionAnimationSpecsFuture.Stub() { @Override - public void sendResult(Bundle data) throws RemoteException { + public AppTransitionAnimationSpec[] get() throws RemoteException { post(new Runnable() { @Override public void run() { - if (animStartedListener != null) { - animStartedListener.onAnimationStarted(); + synchronized (RecentsView.this) { + mAppTransitionAnimationSpecs = getAppTransitionAnimationSpecs(stackView, + clickedTask, offsetX, offsetY, stackScroll, destinationStack); + RecentsView.this.notifyAll(); } } }); + synchronized (RecentsView.this) { + while (mAppTransitionAnimationSpecs == null) { + try { + RecentsView.this.wait(); + } catch (InterruptedException e) {} + } + if (mAppTransitionAnimationSpecs == null) { + return null; + } + AppTransitionAnimationSpec[] specs + = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()]; + return mAppTransitionAnimationSpecs.toArray(specs); + } } }; - - AppTransitionAnimationSpec[] specsArray = - new AppTransitionAnimationSpec[specs.size()]; - try { - WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionMultiThumb( - specs.toArray(specsArray), callback, null, true /* scaleUp */); - - } catch (RemoteException e) { - Log.w(TAG, "Error overriding app transition", e); - } } private List<AppTransitionAnimationSpec> getAppTransitionAnimationSpecs(TaskStackView stackView, @@ -568,30 +559,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV return new AppTransitionAnimationSpec(taskId, b, rect); } - /** - * Cancels any running window transitions for the launched task (the task animating into - * Recents). - */ - private void cancelLaunchedTaskWindowTransition(final Task task) { - SystemServicesProxy ssp = Recents.getSystemServices(); - RecentsConfiguration config = Recents.getConfiguration(); - RecentsActivityLaunchState launchState = config.getLaunchState(); - if (launchState.launchedToTaskId != -1 && - launchState.launchedToTaskId != task.key.id) { - ssp.cancelWindowTransition(launchState.launchedToTaskId); - } - } - /**** TaskStackView.TaskStackCallbacks Implementation ****/ @Override public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv, final TaskStack stack, final Task task, final boolean lockToTask, final boolean boundsValid, final Rect bounds, int destinationStack) { - // Notify any callbacks of the launching of a new task - if (mCb != null) { - mCb.onTaskViewClicked(); - } // Upfront the processing of the thumbnail TaskViewTransform transform = new TaskViewTransform(); @@ -613,8 +586,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // Compute the thumbnail to scale up from final SystemServicesProxy ssp = Recents.getSystemServices(); boolean screenPinningRequested = false; - ActivityOptions opts = null; + ActivityOptions opts = ActivityOptions.makeBasic(); ActivityOptions.OnAnimationStartedListener animStartedListener = null; + final IAppTransitionAnimationSpecsFuture transitionFuture; if (task.thumbnail != null && task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { animStartedListener = new ActivityOptions.OnAnimationStartedListener() { @@ -622,7 +596,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // window transition - cancelLaunchedTaskWindowTransition(task); + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); if (lockToTask) { // Request screen pinning after the animation runs @@ -636,21 +610,18 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } }; - postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll, - animStartedListener, destinationStack); - opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView, - Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(), - offsetX, offsetY, (int) transform.rect.width(), (int) transform.rect.height(), - sourceView.getHandler(), animStartedListener); + transitionFuture = getAppTransitionFuture(stackView, tv, offsetX, offsetY, stackScroll, + destinationStack); screenPinningRequested = true; } else { - opts = ActivityOptions.makeBasic(); + transitionFuture = null; } if (boundsValid) { opts.setBounds(bounds.isEmpty() ? null : bounds); } final ActivityOptions launchOpts = opts; final boolean finalScreenPinningRequested = screenPinningRequested; + final OnAnimationStartedListener finalAnimStartedListener = animStartedListener; final Runnable launchRunnable = new Runnable() { @Override public void run() { @@ -678,6 +649,28 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV MetricsLogger.count(getContext(), "overview_task_launch_failed", 1); } } + if (transitionFuture != null) { + IRemoteCallback.Stub callback = new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + if (finalAnimStartedListener != null) { + finalAnimStartedListener.onAnimationStarted(); + } + } + }); + } + }; + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionMultiThumbFuture(transitionFuture, + callback, true /* scaleUp */); + } catch (RemoteException e) { + Log.w(TAG, "Failed to override transition: " + e); + } + } } }; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 7f5c768d870f..51091c38eec0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -31,7 +31,6 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedList; /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index fc3a6819c953..3ef597f7c849 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -34,7 +34,6 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsActivityLaunchState; @@ -228,6 +227,42 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal return mImmutableTaskViews; } + /** + * Returns the front most task view. + * + * @param stackTasksOnly if set, will return the front most task view in the stack (by default + * the front most task view will be freeform since they are placed above + * stack tasks) + */ + private TaskView getFrontMostTaskView(boolean stackTasksOnly) { + List<TaskView> taskViews = getTaskViews(); + int taskViewCount = taskViews.size(); + for (int i = taskViewCount - 1; i >= 0; i--) { + TaskView tv = taskViews.get(i); + Task task = tv.getTask(); + if (stackTasksOnly && task.isFreeformTask()) { + continue; + } + return tv; + } + return null; + } + + /** + * Finds the child view given a specific {@param task}. + */ + public TaskView getChildViewForTask(Task t) { + List<TaskView> taskViews = getTaskViews(); + int taskViewCount = taskViews.size(); + for (int i = 0; i < taskViewCount; i++) { + TaskView tv = taskViews.get(i); + if (tv.getTask() == t) { + return tv; + } + } + return null; + } + /** Resets this TaskStackView for reuse. */ void reset() { // Reset the focused task @@ -288,19 +323,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } - /** Finds the child view given a specific task. */ - public TaskView getChildViewForTask(Task t) { - List<TaskView> taskViews = getTaskViews(); - int taskViewCount = taskViews.size(); - for (int i = 0; i < taskViewCount; i++) { - TaskView tv = taskViews.get(i); - if (tv.getTask() == t) { - return tv; - } - } - return null; - } - /** Returns the stack algorithm for this task stack. */ public TaskStackLayoutAlgorithm getStackAlgorithm() { return mLayoutAlgorithm; @@ -635,13 +657,49 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** * Sets the focused task relative to the currently focused task. * + * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and + * if the currently focused task is not a stack task, will set the focus + * to the first visible stack task * @param animated determines whether to actually draw the highlight along with the change in * focus. */ - public void setRelativeFocusedTask(boolean forward, boolean animated) { - // Find the next index to focus - int newIndex = mFocusedTaskIndex + (forward ? -1 : 1); - setFocusedTask(newIndex, true, animated); + public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) { + int newIndex = -1; + if (mFocusedTaskIndex != -1) { + if (stackTasksOnly) { + List<Task> tasks = mStack.getTasks(); + newIndex = mFocusedTaskIndex; + Task task = tasks.get(mFocusedTaskIndex); + if (task.isFreeformTask()) { + // Try and focus the front most stack task + TaskView tv = getFrontMostTaskView(stackTasksOnly); + if (tv != null) { + newIndex = mStack.indexOfTask(tv.getTask()); + } + } else { + // Try the next task if it is a stack task + int tmpNewIndex = mFocusedTaskIndex + (forward ? -1 : 1); + if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) { + Task t = tasks.get(tmpNewIndex); + if (!t.isFreeformTask()) { + newIndex = tmpNewIndex; + } + } + } + } else { + // No restrictions, lets just move to the new task + newIndex = mFocusedTaskIndex + (forward ? -1 : 1); + } + } else { + // We don't have a focused task, so focus the first visible task view + TaskView tv = getFrontMostTaskView(stackTasksOnly); + if (tv != null) { + newIndex = mStack.indexOfTask(tv.getTask()); + } + } + if (newIndex != -1) { + setFocusedTask(newIndex, true, animated); + } } /** @@ -703,11 +761,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { - setRelativeFocusedTask(true, false /* animated */); + setRelativeFocusedTask(true, false /* stackTasksOnly */, false /* animated */); return true; } case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { - setRelativeFocusedTask(false, false /* animated */); + setRelativeFocusedTask(false, false /* stackTasksOnly */, false /* animated */); return true; } } @@ -1324,11 +1382,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } public final void onBusEvent(FocusNextTaskViewEvent event) { - setRelativeFocusedTask(true, true); + setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */); } public final void onBusEvent(FocusPreviousTaskViewEvent event) { - setRelativeFocusedTask(false, true); + setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */); } public final void onBusEvent(DismissFocusedTaskViewEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java index a32b2422aa24..45f573d46787 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java @@ -17,7 +17,6 @@ package com.android.systemui.recents.views; import com.android.systemui.R; -import com.android.systemui.recents.Constants; import com.android.systemui.recents.model.Task; import java.util.ArrayList; @@ -113,7 +112,7 @@ public class TaskStackViewFilterAlgorithm { tv.prepareTaskTransformForFilterTaskHidden(fromTransform); tv.updateViewPropertiesToTaskTransform(fromTransform, 0); - toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay; + toTransform.startDelay = offset * 25; childViewTransformsOut.put(tv, toTransform); // Use the movement of the new views to calculate the duration of the animation @@ -166,7 +165,7 @@ public class TaskStackViewFilterAlgorithm { (int) tv.getTranslationY())); } - toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay; + toTransform.startDelay = offset * 25; childViewTransformsOut.put(tv, toTransform); offset++; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 59c9708840e2..81c89a1a0dcc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -31,6 +31,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; +import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.ui.DismissTaskViewEvent; @@ -149,6 +150,11 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { if (mSv.getTaskViews().size() == 0) { return false; } + // Short circuit while we are alt-tabbing + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); + if (launchState.launchedWithAltTab) { + return false; + } final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm; int action = ev.getAction(); @@ -305,9 +311,11 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // Find the front most task and scroll the next task to the front float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL); if (vScroll > 0) { - mSv.setRelativeFocusedTask(true, false /* animated */); + mSv.setRelativeFocusedTask(true, true /* stackTasksOnly */, + false /* animated */); } else { - mSv.setRelativeFocusedTask(false, false /* animated */); + mSv.setRelativeFocusedTask(false, true /* stackTasksOnly */, + false /* animated */); } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index dd1474d9633e..4f4b91ad5691 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -29,6 +29,7 @@ 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.Log; import android.view.MotionEvent; @@ -80,7 +81,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, boolean mTaskDataLoaded; boolean mIsFocused; boolean mIsFocusAnimated; - boolean mFocusAnimationsEnabled; boolean mClipViewInStack; AnimateableViewBounds mViewBounds; @@ -642,6 +642,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, setDim(getDimFromTaskProgress()); } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (Constants.DebugFlags.App.EnableFastToggleRecents && mIsFocused) { + Paint tmpPaint = new Paint(); + Rect tmpRect = new Rect(); + tmpRect.set(0, 0, getWidth(), getHeight()); + tmpPaint.setColor(0xFFFF0000); + tmpPaint.setStrokeWidth(35); + tmpPaint.setStyle(Paint.Style.STROKE); + canvas.drawRect(tmpRect, tmpPaint); + } + } + /**** View focus state ****/ /** @@ -672,6 +687,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, clearAccessibilityFocus(); } } + if (Constants.DebugFlags.App.EnableFastToggleRecents) { + invalidate(); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java new file mode 100644 index 000000000000..dd894ce418a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.stackdivider; + +import android.content.res.Configuration; +import android.view.LayoutInflater; + +import com.android.systemui.R; +import com.android.systemui.SystemUI; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +/** + * Controls the docked stack divider. + */ +public class Divider extends SystemUI { + private static final String TAG = "Divider"; + private int mDividerWindowWidth; + private DividerWindowManager mWindowManager; + + @Override + public void start() { + mWindowManager = new DividerWindowManager(mContext); + mDividerWindowWidth = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_thickness); + update(mContext.getResources().getConfiguration()); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + update(newConfig); + } + + private void addDivider(Configuration configuration) { + DividerView view = (DividerView) + LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null); + final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE; + final int width = landscape ? mDividerWindowWidth : MATCH_PARENT; + final int height = landscape ? MATCH_PARENT : mDividerWindowWidth; + mWindowManager.add(view, width, height); + view.setWindowManager(mWindowManager); + } + + private void removeDivider() { + mWindowManager.remove(); + } + + private void update(Configuration configuration) { + removeDivider(); + addDivider(configuration); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java new file mode 100644 index 000000000000..5f983c5435e1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.stackdivider; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.DisplayInfo; + +import com.android.systemui.statusbar.FlingAnimationUtils; + +import java.util.ArrayList; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + +/** + * Calculates the snap targets and the snap position given a position and a velocity. All positions + * here are to be interpreted as the left/top edge of the divider rectangle. + */ +public class DividerSnapAlgorithm { + + private final Context mContext; + private final FlingAnimationUtils mFlingAnimationUtils; + private final int mDividerSize; + private final ArrayList<SnapTarget> mTargets; + + /** The first target which is still splitting the screen */ + private final SnapTarget mFirstSplitTarget; + + /** The last target which is still splitting the screen */ + private final SnapTarget mLastSplitTarget; + + private final SnapTarget mDismissStartTarget; + private final SnapTarget mDismissEndTarget; + + public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils, + int dividerSize, boolean isHorizontalDivision) { + mContext = ctx; + mFlingAnimationUtils = flingAnimationUtils; + mDividerSize = dividerSize; + mTargets = calculateTargets(isHorizontalDivision); + mFirstSplitTarget = mTargets.get(1); + mLastSplitTarget = mTargets.get(mTargets.size() - 2); + mDismissStartTarget = mTargets.get(0); + mDismissEndTarget = mTargets.get(mTargets.size() - 1); + } + + public SnapTarget calculateSnapTarget(int position, float velocity) { + if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { + return snap(position); + } + if (position < mFirstSplitTarget.position && velocity < 0) { + return mDismissStartTarget; + } + if (position > mLastSplitTarget.position && velocity > 0) { + return mDismissEndTarget; + } + if (velocity < 0) { + return mFirstSplitTarget; + } else { + return mLastSplitTarget; + } + } + + private SnapTarget snap(int position) { + int minIndex = -1; + int minDistance = Integer.MAX_VALUE; + int size = mTargets.size(); + for (int i = 0; i < size; i++) { + int distance = Math.abs(position - mTargets.get(i).position); + if (distance < minDistance) { + minIndex = i; + minDistance = distance; + } + } + return mTargets.get(minIndex); + } + + private ArrayList<SnapTarget> calculateTargets(boolean isHorizontalDivision) { + ArrayList<SnapTarget> targets = new ArrayList<>(); + DisplayMetrics info = mContext.getResources().getDisplayMetrics(); + int dividerMax = isHorizontalDivision + ? info.heightPixels + : info.widthPixels; + + // TODO: Better calculation + targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START)); + targets.add(new SnapTarget((int) (0.35f * dividerMax) - mDividerSize / 2, + SnapTarget.FLAG_NONE)); + targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE)); + targets.add(new SnapTarget((int) (0.65f * dividerMax) - mDividerSize / 2, + SnapTarget.FLAG_NONE)); + targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END)); + return targets; + } + + /** + * Represents a snap target for the divider. + */ + public static class SnapTarget { + public static final int FLAG_NONE = 0; + + /** If the divider reaches this value, the left/top task should be dismissed. */ + public static final int FLAG_DISMISS_START = 1; + + /** If the divider reaches this value, the right/bottom task should be dismissed */ + public static final int FLAG_DISMISS_END = 2; + + public final int position; + public final int flag; + + public SnapTarget(int position, int flag) { + this.position = position; + this.flag = flag; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java new file mode 100644 index 000000000000..59d4011446f5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.stackdivider; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.graphics.Region.Op; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.ViewTreeObserver.InternalInsetsInfo; +import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; +import android.view.WindowManager; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageButton; + +import com.android.systemui.R; +import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget; +import com.android.systemui.statusbar.FlingAnimationUtils; + +import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW; +import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW; + +/** + * Docked stack divider. + */ +public class DividerView extends FrameLayout implements OnTouchListener, + OnComputeInternalInsetsListener { + + private static final String TAG = "DividerView"; + + private ImageButton mHandle; + private View mBackground; + private int mStartX; + private int mStartY; + private int mStartPosition; + private int mDockSide; + private final int[] mTempInt2 = new int[2]; + + private int mDividerInsets; + private int mDisplayWidth; + private int mDisplayHeight; + private int mDividerWindowWidth; + private int mDividerSize; + private int mTouchElevation; + + private final Rect mTmpRect = new Rect(); + private final Rect mLastResizeRect = new Rect(); + private final WindowManagerProxy mWindowManagerProxy = new WindowManagerProxy(); + private Interpolator mFastOutSlowInInterpolator; + private final Interpolator mTouchResponseInterpolator = + new PathInterpolator(0.3f, 0f, 0.1f, 1f); + private DividerWindowManager mWindowManager; + private VelocityTracker mVelocityTracker; + private FlingAnimationUtils mFlingAnimationUtils; + + public DividerView(Context context) { + super(context); + } + + public DividerView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mHandle = (ImageButton) findViewById(R.id.docked_divider_handle); + mBackground = findViewById(R.id.docked_divider_background); + mHandle.setOnTouchListener(this); + mDividerWindowWidth = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_thickness); + mDividerInsets = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_insets); + mDividerSize = mDividerWindowWidth - 2 * mDividerInsets; + mTouchElevation = getResources().getDimensionPixelSize( + R.dimen.docked_stack_divider_lift_elevation); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), + android.R.interpolator.fast_out_slow_in); + mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f); + updateDisplayInfo(); + boolean landscape = getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; + mHandle.setPointerShape( + landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW); + getViewTreeObserver().addOnComputeInternalInsetsListener(this); + } + + public void setWindowManager(DividerWindowManager windowManager) { + mWindowManager = windowManager; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + convertToScreenCoordinates(event); + final int action = event.getAction() & MotionEvent.ACTION_MASK; + switch (action) { + case MotionEvent.ACTION_DOWN: + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(event); + mStartX = (int) event.getX(); + mStartY = (int) event.getY(); + getLocationOnScreen(mTempInt2); + mDockSide = mWindowManagerProxy.getDockSide(); + if (isHorizontalDivision()) { + mStartPosition = mTempInt2[1] + mDividerInsets; + } else { + mStartPosition = mTempInt2[0] + mDividerInsets; + } + if (mDockSide != WindowManager.DOCKED_INVALID) { + mWindowManagerProxy.setResizing(true); + mWindowManager.setSlippery(false); + liftBackground(); + return true; + } else { + return false; + } + case MotionEvent.ACTION_MOVE: + mVelocityTracker.addMovement(event); + int x = (int) event.getX(); + int y = (int) event.getY(); + if (mDockSide != WindowManager.DOCKED_INVALID) { + resizeStack(calculatePosition(x, y)); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mVelocityTracker.addMovement(event); + + x = (int) event.getRawX(); + y = (int) event.getRawY(); + + mVelocityTracker.computeCurrentVelocity(1000); + fling(x, y, mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + + mWindowManager.setSlippery(true); + releaseBackground(); + break; + } + return true; + } + + private void convertToScreenCoordinates(MotionEvent event) { + event.setLocation(event.getRawX(), event.getRawY()); + } + + private void fling(int x, int y, float xVelocity, float yVelocity) { + int position = calculatePosition(x, y); + float velocity = isHorizontalDivision() ? yVelocity : xVelocity; + final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, + mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity); + + ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); + anim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + resizeStack((Integer) animation.getAnimatedValue()); + } + }); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + commitSnapFlags(snapTarget); + mWindowManagerProxy.setResizing(false); + mDockSide = WindowManager.DOCKED_INVALID; + } + }); + mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity); + anim.start(); + } + + private void commitSnapFlags(SnapTarget target) { + if (target.flag == SnapTarget.FLAG_NONE) { + return; + } + boolean dismissOrMaximize; + if (target.flag == SnapTarget.FLAG_DISMISS_START) { + dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT + || mDockSide == WindowManager.DOCKED_TOP; + } else { + dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT + || mDockSide == WindowManager.DOCKED_BOTTOM; + } + if (dismissOrMaximize) { + mWindowManagerProxy.dismissDockedStack(); + } else { + mWindowManagerProxy.maximizeDockedStack(); + } + } + + private void liftBackground() { + if (isHorizontalDivision()) { + mBackground.animate().scaleY(1.5f); + } else { + mBackground.animate().scaleX(1.5f); + } + mBackground.animate() + .setInterpolator(mTouchResponseInterpolator) + .setDuration(150) + .translationZ(mTouchElevation); + + // Lift handle as well so it doesn't get behind the background, even though it doesn't + // cast shadow. + mHandle.animate() + .setInterpolator(mTouchResponseInterpolator) + .setDuration(150) + .translationZ(mTouchElevation); + } + + private void releaseBackground() { + mBackground.animate() + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(200) + .translationZ(0) + .scaleX(1f) + .scaleY(1f); + mHandle.animate() + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(200) + .translationZ(0); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateDisplayInfo(); + } + + private void updateDisplayInfo() { + DisplayMetrics info = mContext.getResources().getDisplayMetrics(); + mDisplayWidth = info.widthPixels; + mDisplayHeight = info.heightPixels; + } + + private int calculatePosition(int touchX, int touchY) { + return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX); + } + + private boolean isHorizontalDivision() { + return mDockSide == WindowManager.DOCKED_TOP + || mDockSide == WindowManager.DOCKED_BOTTOM; + } + + private int calculateXPosition(int touchX) { + return mStartPosition + touchX - mStartX; + } + + private int calculateYPosition(int touchY) { + return mStartPosition + touchY - mStartY; + } + + private void resizeStack(int position) { + mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight); + switch (mDockSide) { + case WindowManager.DOCKED_LEFT: + mTmpRect.right = position; + break; + case WindowManager.DOCKED_TOP: + mTmpRect.bottom = position; + break; + case WindowManager.DOCKED_RIGHT: + mTmpRect.left = position + mDividerWindowWidth - 2 * mDividerInsets; + break; + case WindowManager.DOCKED_BOTTOM: + mTmpRect.top = position + mDividerWindowWidth - 2 * mDividerInsets; + break; + } + if (mTmpRect.equals(mLastResizeRect)) { + return; + } + + // Make sure shadows are updated + mBackground.invalidate(); + + mLastResizeRect.set(mTmpRect); + mWindowManagerProxy.resizeDockedStack(mTmpRect); + } + + @Override + public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { + inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), + mHandle.getBottom()); + inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(), + mBackground.getRight(), mBackground.getBottom(), Op.UNION); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java new file mode 100644 index 000000000000..225187455f3f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.stackdivider; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.view.View; +import android.view.WindowManager; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; + +/** + * Manages the window parameters of the docked stack divider. + */ +public class DividerWindowManager { + + private static final String WINDOW_TITLE = "DockedStackDivider"; + + private final WindowManager mWindowManager; + private WindowManager.LayoutParams mLp; + private View mView; + + public DividerWindowManager(Context ctx) { + mWindowManager = ctx.getSystemService(WindowManager.class); + } + + public void add(View view, int width, int height) { + mLp = new WindowManager.LayoutParams( + width, height, TYPE_DOCK_DIVIDER, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL + | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + mLp.setTitle(WINDOW_TITLE); + mWindowManager.addView(view, mLp); + mView = view; + } + + public void remove() { + if (mView != null) { + mWindowManager.removeView(mView); + } + mView = null; + } + + public void setSlippery(boolean slippery) { + boolean changed = false; + if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) { + mLp.flags |= FLAG_SLIPPERY; + changed = true; + } else if (!slippery && (mLp.flags & FLAG_SLIPPERY) != 0) { + mLp.flags &= ~FLAG_SLIPPERY; + changed = true; + } + if (changed) { + mWindowManager.updateViewLayout(mView, mLp); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java new file mode 100644 index 000000000000..94809bc664b1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.stackdivider; + +import android.app.ActivityManagerNative; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.WindowManagerGlobal; + +import com.android.internal.annotations.GuardedBy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.view.WindowManager.DOCKED_INVALID; + +/** + * Proxy to simplify calls into window manager/activity manager + */ +public class WindowManagerProxy { + + private static final String TAG = "WindowManagerProxy"; + + @GuardedBy("mResizeRect") + private final Rect mResizeRect = new Rect(); + private final Rect mTmpRect = new Rect(); + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + + private final Runnable mResizeRunnable = new Runnable() { + @Override + public void run() { + synchronized (mResizeRect) { + mTmpRect.set(mResizeRect); + } + try { + ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, + mTmpRect, true); + } catch (RemoteException e) { + Log.w(TAG, "Failed to resize stack: " + e); + } + } + }; + + public void resizeDockedStack(Rect rect) { + synchronized (mResizeRect) { + mResizeRect.set(rect); + } + mExecutor.execute(mResizeRunnable); + } + + public void dismissDockedStack() { + try { + ActivityManagerNative.getDefault().removeStack(DOCKED_STACK_ID); + } catch (RemoteException e) { + Log.w(TAG, "Failed to remove stack: " + e); + } + } + + public void maximizeDockedStack() { + try { + ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true); + } catch (RemoteException e) { + Log.w(TAG, "Failed to resize stack: " + e); + } + } + + public void setResizing(boolean resizing) { + try { + WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing); + } catch (RemoteException e) { + Log.w(TAG, "Error calling setDockedStackResizing: " + e); + } + } + + public int getDockSide() { + try { + return WindowManagerGlobal.getWindowManagerService().getDockedStackSide(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get dock side: " + e); + } + return DOCKED_INVALID; + } +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e27dba6fa2f7..e13d3e7d7fa9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17444,14 +17444,16 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("Removing home stack is not allowed."); } synchronized (this) { + long origId = Binder.clearCallingIdentity(); ActivityStack stack = mStackSupervisor.getStack(stackId); if (stack != null) { ArrayList<TaskRecord> tasks = stack.getAllTasks(); for (int i = tasks.size() - 1; i >= 0; i--) { - removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */, + removeTaskByIdLocked(tasks.get(i).taskId, false /* killProcess */, !REMOVE_FROM_RECENTS); } } + Binder.restoreCallingIdentity(origId); } } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index c35b91ffb4f0..031f6d2016b4 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -319,7 +319,7 @@ final class TaskRecord { mCallingPackage = callingPackage; mResizeable = resizeable || mService.mForceResizableActivites; mPrivileged = privileged; - ActivityInfo info = mActivities.get(0).info; + ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null; mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d89f47cc622d..058d681c6ac9 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -76,6 +76,7 @@ import android.media.audiopolicy.AudioPolicyConfig; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; @@ -88,6 +89,8 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.System; @@ -397,6 +400,12 @@ public class AudioService extends IAudioService.Stub { // Broadcast receiver for device connections intent broadcasts private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); + /** Interface for UserManagerService. */ + private final UserManagerInternal mUserManagerInternal; + + private final UserRestrictionsListener mUserRestrictionsListener = + new AudioServiceUserRestrictionsListener(); + // Devices currently connected // Use makeDeviceListKey() to make a unique key for this list. private class DeviceListSpec { @@ -598,6 +607,8 @@ public class AudioService extends IAudioService.Stub { mPlatformType = AudioSystem.getPlatformType(context); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent"); @@ -694,6 +705,8 @@ public class AudioService extends IAudioService.Stub { context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); + + mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); } public void systemReady() { @@ -1039,17 +1052,32 @@ public class AudioService extends IAudioService.Stub { System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED, UserHandle.USER_CURRENT); - boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE, - 0, UserHandle.USER_CURRENT) == 1; + final int currentUser = getCurrentUserId(); + + // In addition to checking the system setting, also check the current user restriction. + // Because of the delay before persisting VOLUME_MASTER_MUTE, there's a window where + // DISALLOW_ADJUST_VOLUME will be ignored when it's set right before switching users. + boolean masterMute = (System.getIntForUser(cr, System.VOLUME_MASTER_MUTE, + 0, UserHandle.USER_CURRENT) == 1) + || mUserManagerInternal.getUserRestriction( + currentUser, UserManager.DISALLOW_ADJUST_VOLUME); if (mUseFixedVolume) { masterMute = false; AudioSystem.setMasterVolume(1.0f); } + if (DEBUG_VOL) { + Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser)); + } AudioSystem.setMasterMute(masterMute); broadcastMasterMuteStatus(masterMute); boolean microphoneMute = - System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1; + (System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1) + || mUserManagerInternal.getUserRestriction( + currentUser, UserManager.DISALLOW_UNMUTE_MICROPHONE); + if (DEBUG_VOL) { + Log.d(TAG, String.format("Mic mute %s, user=%d", microphoneMute, currentUser)); + } AudioSystem.muteMicrophone(microphoneMute); // Each stream will read its own persisted settings @@ -1767,6 +1795,16 @@ public class AudioService extends IAudioService.Stub { != PackageManager.PERMISSION_GRANTED) { return; } + setMasterMuteInternalNoCallerCheck(mute, flags, userId); + } + + private void setMasterMuteInternalNoCallerCheck(boolean mute, int flags, int userId) { + if (DEBUG_VOL) { + Log.d(TAG, String.format("Master mute %s, %d, user=%d", mute, flags, userId)); + } + if (mUseFixedVolume) { + return; // If using fixed volume, we don't mute. + } if (getCurrentUserId() == userId) { if (mute != AudioSystem.getMasterMute()) { setSystemAudioMute(mute); @@ -1841,7 +1879,8 @@ public class AudioService extends IAudioService.Stub { return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM]; } - /** @see AudioManager#setMicrophoneMute(boolean, int) */ + /** @see AudioManager#setMicrophoneMute(boolean) */ + @Override public void setMicrophoneMute(boolean on, String callingPackage, int userId) { // If we are being called by the system check for user we are going to change // so we handle user restrictions correctly. @@ -1863,7 +1902,13 @@ public class AudioService extends IAudioService.Stub { != PackageManager.PERMISSION_GRANTED) { return; } + setMicrophoneMuteNoCallerCheck(on, userId); + } + private void setMicrophoneMuteNoCallerCheck(boolean on, int userId) { + if (DEBUG_VOL) { + Log.d(TAG, String.format("Mic mute %s, user=%d", on, userId)); + } // If mute is for current user actually mute, else just persist the setting // which will be loaded on user switch. if (getCurrentUserId() == userId) { @@ -5116,6 +5161,35 @@ public class AudioService extends IAudioService.Stub { } } // end class AudioServiceBroadcastReceiver + private class AudioServiceUserRestrictionsListener implements UserRestrictionsListener { + + @Override + public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, + Bundle prevRestrictions) { + // Update mic mute state. + { + final boolean wasRestricted = + prevRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE); + final boolean isRestricted = + newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE); + if (wasRestricted != isRestricted) { + setMicrophoneMuteNoCallerCheck(isRestricted, userId); + } + } + + // Update speaker mute state. + { + final boolean wasRestricted = + prevRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME); + final boolean isRestricted = + newRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME); + if (wasRestricted != isRestricted) { + setMasterMuteInternalNoCallerCheck(isRestricted, /* flags =*/ 0, userId); + } + } + } + } // end class AudioServiceUserRestrictionsListener + private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) { PackageManager pm = mContext.getPackageManager(); // Find the home activity of the user. It should not be killed to avoid expensive restart, diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 80fd82330fca..e4dbf65764d1 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -20,6 +20,7 @@ import android.Manifest; import android.app.DownloadManager; import android.app.admin.DevicePolicyManager; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal.PackagesProvider; @@ -696,7 +697,12 @@ final class DefaultPermissionGrantPolicy { Intent intent, int userId) { ResolveInfo handler = mService.resolveIntent(intent, intent.resolveType(mService.mContext.getContentResolver()), 0, userId); - if (handler == null) { + if (handler == null || handler.activityInfo == null) { + return null; + } + ActivityInfo activityInfo = handler.activityInfo; + if (activityInfo.packageName.equals(mService.mResolveActivity.packageName) + && activityInfo.name.equals(mService.mResolveActivity.name)) { return null; } return getSystemPackageLPr(handler.activityInfo.packageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index c259ac2b2fec..d7176fd0776b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -1,28 +1,68 @@ +/* + * Copyright (C) 2015 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 com.android.server.pm; +import android.app.ActivityManager; import android.content.ComponentName; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageInstaller.SessionParams; import android.content.res.AssetManager; import android.content.res.Resources; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; +import android.text.TextUtils; + +import com.android.internal.util.SizedInputStream; + +import libcore.io.IoUtils; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.WeakHashMap; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; class PackageManagerShellCommand extends ShellCommand { final IPackageManager mInterface; @@ -42,8 +82,21 @@ class PackageManagerShellCommand extends ShellCommand { final PrintWriter pw = getOutPrintWriter(); try { switch(cmd) { + case "install": + return runInstall(); + case "install-abandon": + case "install-destroy": + return runInstallAbandon(); + case "install-commit": + return runInstallCommit(); + case "install-create": + return runInstallCreate(); + case "install-write": + return runInstallWrite(); case "list": return runList(); + case "uninstall": + return runUninstall(); default: return handleDefaultCommands(cmd); } @@ -53,6 +106,65 @@ class PackageManagerShellCommand extends ShellCommand { return -1; } + private int runInstall() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final InstallParams params = makeInstallParams(); + final int sessionId = doCreateSession(params.sessionParams, + params.installerPackageName, params.userId); + + final String inPath = getNextArg(); + if (inPath == null && params.sessionParams.sizeBytes == 0) { + pw.println("Error: must either specify a package size or an APK file"); + return 1; + } + if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk") != 0) { + return 1; + } + if (doCommitSession(sessionId) != 0) { + return 1; + } + return 0; + } + + private int runInstallAbandon() throws RemoteException { + final int sessionId = Integer.parseInt(getNextArg()); + return doAbandonSession(sessionId); + } + + private int runInstallCommit() throws RemoteException { + final int sessionId = Integer.parseInt(getNextArg()); + return doCommitSession(sessionId); + } + + private int runInstallCreate() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final InstallParams installParams = makeInstallParams(); + final int sessionId = doCreateSession(installParams.sessionParams, + installParams.installerPackageName, installParams.userId); + + // NOTE: adb depends on parsing this string + pw.println("Success: created install session [" + sessionId + "]"); + return 0; + } + + private int runInstallWrite() throws RemoteException { + long sizeBytes = -1; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("-S")) { + sizeBytes = Long.parseLong(getNextArg()); + } else { + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + + final int sessionId = Integer.parseInt(getNextArg()); + final String splitName = getNextArg(); + final String path = getNextArg(); + return doWriteSession(sessionId, path, sizeBytes, splitName); + } + private int runList() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final String type = getNextArg(); @@ -363,6 +475,279 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runUninstall() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int flags = 0; + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-k": + flags |= PackageManager.DELETE_KEEP_DATA; + break; + case "--user": + userId = Integer.parseInt(getNextArg()); + break; + default: + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + + String packageName = getNextArg(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + + userId = translateUserId(userId, "runUninstall"); + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_SYSTEM; + flags |= PackageManager.DELETE_ALL_USERS; + } else { + final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId); + if (info == null) { + pw.println("Failure - not installed for " + userId); + return 1; + } + final boolean isSystem = + (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + // If we are being asked to delete a system app for just one + // user set flag so it disables rather than reverting to system + // version of the app. + if (isSystem) { + flags |= PackageManager.DELETE_SYSTEM_APP; + } + } + + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags, + receiver.getIntentSender(), userId); + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + pw.println("Success"); + return 0; + } else { + pw.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + return 1; + } + } + + private static class InstallParams { + SessionParams sessionParams; + String installerPackageName; + int userId = UserHandle.USER_ALL; + } + + private InstallParams makeInstallParams() { + final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL); + final InstallParams params = new InstallParams(); + params.sessionParams = sessionParams; + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-l": + sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK; + break; + case "-r": + sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; + break; + case "-i": + params.installerPackageName = getNextArg(); + if (params.installerPackageName == null) { + throw new IllegalArgumentException("Missing installer package"); + } + break; + case "-t": + sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST; + break; + case "-s": + sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL; + break; + case "-f": + sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL; + break; + case "-d": + sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; + break; + case "-g": + sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; + break; + case "--originating-uri": + sessionParams.originatingUri = Uri.parse(getNextArg()); + break; + case "--referrer": + sessionParams.referrerUri = Uri.parse(getNextArg()); + break; + case "-p": + sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING; + sessionParams.appPackageName = getNextArg(); + if (sessionParams.appPackageName == null) { + throw new IllegalArgumentException("Missing inherit package name"); + } + break; + case "-S": + sessionParams.setSize(Long.parseLong(getNextArg())); + break; + case "--abi": + sessionParams.abiOverride = checkAbiArgument(getNextArg()); + break; + case "--user": + params.userId = Integer.parseInt(getNextArg()); + break; + case "--install-location": + sessionParams.installLocation = Integer.parseInt(getNextArg()); + break; + case "--force-uuid": + sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID; + sessionParams.volumeUuid = getNextArg(); + if ("internal".equals(sessionParams.volumeUuid)) { + sessionParams.volumeUuid = null; + } + break; + default: + throw new IllegalArgumentException("Unknown option " + opt); + } + } + return params; + } + + private static String checkAbiArgument(String abi) { + if (TextUtils.isEmpty(abi)) { + throw new IllegalArgumentException("Missing ABI argument"); + } + + if ("-".equals(abi)) { + return abi; + } + + final String[] supportedAbis = Build.SUPPORTED_ABIS; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + return abi; + } + } + + throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); + } + + private int translateUserId(int userId, String logContext) { + return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, true, true, logContext, "pm command"); + } + + private int doCreateSession(SessionParams params, String installerPackageName, int userId) + throws RemoteException { + userId = translateUserId(userId, "runInstallCreate"); + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_SYSTEM; + params.installFlags |= PackageManager.INSTALL_ALL_USERS; + } + + final int sessionId = mInterface.getPackageInstaller() + .createSession(params, installerPackageName, userId); + return sessionId; + } + + private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName) + throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + if ("-".equals(inPath)) { + inPath = null; + } else if (inPath != null) { + final File file = new File(inPath); + if (file.isFile()) { + sizeBytes = file.length(); + } + } + + final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId); + + PackageInstaller.Session session = null; + InputStream in = null; + OutputStream out = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + + if (inPath != null) { + in = new FileInputStream(inPath); + } else { + in = new SizedInputStream(getInputStream(), sizeBytes); + } + out = session.openWrite(splitName, 0, sizeBytes); + + int total = 0; + byte[] buffer = new byte[65536]; + int c; + while ((c = in.read(buffer)) != -1) { + total += c; + out.write(buffer, 0, c); + + if (info.sizeBytes > 0) { + final float fraction = ((float) c / (float) info.sizeBytes); + session.addProgress(fraction); + } + } + session.fsync(out); + + pw.println("Success: streamed " + total + " bytes"); + return 0; + } catch (IOException e) { + pw.println("Error: failed to write; " + e.getMessage()); + return 1; + } finally { + IoUtils.closeQuietly(out); + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(session); + } + } + + private int doCommitSession(int sessionId) throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + session.commit(receiver.getIntentSender()); + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + pw.println("Success"); + } else { + pw.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + pw.println("Failure details: " + result.getExtras()); + } + return status; + } finally { + IoUtils.closeQuietly(session); + } + } + + private int doAbandonSession(int sessionId) throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session( + mInterface.getPackageInstaller().openSession(sessionId)); + session.abandon(); + pw.println("Success"); + return 0; + } finally { + IoUtils.closeQuietly(session); + } + } + private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels, boolean summary, int startProtectionLevel, int endProtectionLevel) throws RemoteException { @@ -525,5 +910,34 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" -u: list only the permissions users will see"); pw.println(""); } + + private static class LocalIntentReceiver { + private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>(); + + private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { + @Override + public int send(int code, Intent intent, String resolvedType, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + try { + mResult.offer(intent, 5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return 0; + } + }; + + public IntentSender getIntentSender() { + return new IntentSender((IIntentSender) mLocalSender); + } + + public Intent getResult() { + try { + return mResult.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 621ae2d56e7c..4929960bad93 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -52,6 +52,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.system.ErrnoException; @@ -232,6 +233,10 @@ public class UserManagerService extends IUserManager.Stub { private final LocalService mLocalService; + @GuardedBy("mUserRestrictionsListeners") + private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners = + new ArrayList<>(); + private static UserManagerService sInstance; public static UserManagerService getInstance() { @@ -630,7 +635,7 @@ public class UserManagerService extends IUserManager.Stub { } if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) { info.flags |= UserInfo.FLAG_INITIALIZED; - scheduleWriteUserLP(info); + scheduleWriteUser(info); } } } @@ -770,6 +775,7 @@ public class UserManagerService extends IUserManager.Stub { Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId) != newRestrictions); mBaseUserRestrictions.put(userId, newRestrictions); + scheduleWriteUser(mUsers.get(userId)); } final Bundle effective = computeEffectiveUserRestrictionsLR(userId); @@ -781,7 +787,14 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mRestrictionsLock") private void applyUserRestrictionsLR(int userId, Bundle newRestrictions) { - final Bundle prevRestrictions = mAppliedUserRestrictions.get(userId); + if (newRestrictions == null) { + newRestrictions = Bundle.EMPTY; + } + + Bundle prevRestrictions = mAppliedUserRestrictions.get(userId); + if (prevRestrictions == null) { + prevRestrictions = Bundle.EMPTY; + } if (DBG) { Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId @@ -797,12 +810,36 @@ public class UserManagerService extends IUserManager.Stub { Binder.restoreCallingIdentity(token); } - UserRestrictionsUtils.applyUserRestrictions( + UserRestrictionsUtils.applyUserRestrictionsLR( mContext, userId, newRestrictions, prevRestrictions); + notifyUserRestrictionsListeners(userId, newRestrictions, prevRestrictions); + mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions)); } + private void notifyUserRestrictionsListeners(final int userId, + Bundle newRestrictions, Bundle prevRestrictions) { + + final Bundle newRestrictionsFinal = new Bundle(newRestrictions); + final Bundle prevRestrictionsFinal = new Bundle(prevRestrictions); + + mHandler.post(new Runnable() { + @Override + public void run() { + final UserRestrictionsListener[] listeners; + synchronized (mUserRestrictionsListeners) { + listeners = new UserRestrictionsListener[mUserRestrictionsListeners.size()]; + mUserRestrictionsListeners.toArray(listeners); + } + for (int i = 0; i < listeners.length; i++) { + listeners[i].onUserRestrictionsChanged(userId, + newRestrictionsFinal, prevRestrictionsFinal); + } + } + }); + } + @GuardedBy("mRestrictionsLock") private void updateEffectiveUserRestrictionsLR(int userId) { updateUserRestrictionsInternalLR(null, userId); @@ -1046,7 +1083,7 @@ public class UserManagerService extends IUserManager.Stub { UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM); if ("Primary".equals(user.name)) { user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name); - scheduleWriteUserLP(user); + scheduleWriteUser(user); } userVersion = 1; } @@ -1056,7 +1093,7 @@ public class UserManagerService extends IUserManager.Stub { UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM); if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) { user.flags |= UserInfo.FLAG_INITIALIZED; - scheduleWriteUserLP(user); + scheduleWriteUser(user); } userVersion = 2; } @@ -1080,7 +1117,7 @@ public class UserManagerService extends IUserManager.Stub { if (!splitSystemUser && user.isRestricted() && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) { user.restrictedProfileParentId = UserHandle.USER_SYSTEM; - scheduleWriteUserLP(user); + scheduleWriteUser(user); } } } @@ -1125,7 +1162,9 @@ public class UserManagerService extends IUserManager.Stub { writeUserLP(system); } - private void scheduleWriteUserLP(UserInfo userInfo) { + private void scheduleWriteUser(UserInfo userInfo) { + // No need to wrap it within a lock -- worst case, we'll just post the same message + // twice. if (!mHandler.hasMessages(WRITE_USER_MSG, userInfo)) { Message msg = mHandler.obtainMessage(WRITE_USER_MSG, userInfo); mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY); @@ -1507,7 +1546,7 @@ public class UserManagerService extends IUserManager.Stub { if (isManagedProfile) { if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { parent.profileGroupId = parent.id; - scheduleWriteUserLP(parent); + scheduleWriteUser(parent); } userInfo.profileGroupId = parent.profileGroupId; } else if (isRestricted) { @@ -1516,7 +1555,7 @@ public class UserManagerService extends IUserManager.Stub { } if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) { parent.restrictedProfileParentId = parent.id; - scheduleWriteUserLP(parent); + scheduleWriteUser(parent); } userInfo.restrictedProfileParentId = parent.restrictedProfileParentId; } @@ -1535,7 +1574,7 @@ public class UserManagerService extends IUserManager.Stub { } mPm.createNewUserLILPw(userId); userInfo.partial = false; - scheduleWriteUserLP(userInfo); + scheduleWriteUser(userInfo); updateUserIds(); Bundle restrictions = new Bundle(); synchronized (mRestrictionsLock) { @@ -2112,7 +2151,7 @@ public class UserManagerService extends IUserManager.Stub { } if (now > EPOCH_PLUS_30_YEARS) { user.lastLoggedInTime = now; - scheduleWriteUserLP(user); + scheduleWriteUser(user); } } } @@ -2388,6 +2427,25 @@ public class UserManagerService extends IUserManager.Stub { } } } + + @Override + public boolean getUserRestriction(int userId, String key) { + return getUserRestrictions(userId).getBoolean(key); + } + + @Override + public void addUserRestrictionsListener(UserRestrictionsListener listener) { + synchronized (mUserRestrictionsListeners) { + mUserRestrictionsListeners.add(listener); + } + } + + @Override + public void removeUserRestrictionsListener(UserRestrictionsListener listener) { + synchronized (mUserRestrictionsListeners) { + mUserRestrictionsListeners.remove(listener); + } + } } private class Shell extends ShellCommand { diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 28df9f673ed0..56e8b3eaea2c 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -18,19 +18,14 @@ package com.android.server.pm; import com.google.android.collect.Sets; -import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; -import android.media.IAudioService; import android.net.Uri; import android.os.Binder; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.util.Slog; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -39,6 +34,11 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Set; +/** + * Utility methods for uesr restrictions. + * + * <p>See {@link UserManagerService} for the method suffixes. + */ public class UserRestrictionsUtils { private static final String TAG = "UserRestrictionsUtils"; @@ -129,26 +129,31 @@ public class UserRestrictionsUtils { /** * Takes a new use restriction set and the previous set, and apply the restrictions that have * changed. + * + * <p>Note this method is called by {@link UserManagerService} while holding + * {@code mRestrictionLock}. Be aware when calling into other services, which could cause + * a deadlock. */ - public static void applyUserRestrictions(Context context, int userId, - @Nullable Bundle newRestrictions, @Nullable Bundle prevRestrictions) { - if (newRestrictions == null) { - newRestrictions = Bundle.EMPTY; - } - if (prevRestrictions == null) { - prevRestrictions = Bundle.EMPTY; - } + public static void applyUserRestrictionsLR(Context context, int userId, + Bundle newRestrictions, Bundle prevRestrictions) { for (String key : USER_RESTRICTIONS) { final boolean newValue = newRestrictions.getBoolean(key); final boolean prevValue = prevRestrictions.getBoolean(key); if (newValue != prevValue) { - applyUserRestriction(context, userId, key, newValue); + applyUserRestrictionLR(context, userId, key, newValue); } } } - private static void applyUserRestriction(Context context, int userId, String key, + /** + * Apply each user restriction. + * + * <p>Note this method is called by {@link UserManagerService} while holding + * {@code mRestrictionLock}. Be aware when calling into other services, which could cause + * a deadlock. + */ + private static void applyUserRestrictionLR(Context context, int userId, String key, boolean newValue) { // When certain restrictions are cleared, we don't update the system settings, // because these settings are changeable on the Settings UI and we don't know the original @@ -161,14 +166,6 @@ public class UserRestrictionsUtils { final long id = Binder.clearCallingIdentity(); try { switch (key) { - case UserManager.DISALLOW_UNMUTE_MICROPHONE: - IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE)) - .setMicrophoneMute(newValue, context.getPackageName(), userId); - break; - case UserManager.DISALLOW_ADJUST_VOLUME: - IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE)) - .setMasterMute(newValue, 0, context.getPackageName(), userId); - break; case UserManager.DISALLOW_CONFIG_WIFI: if (newValue) { android.provider.Settings.Secure.putIntForUser(cr, @@ -231,8 +228,6 @@ public class UserRestrictionsUtils { } break; } - } catch (RemoteException re) { - Slog.e(TAG, "Failed to talk to AudioService.", re); } finally { Binder.restoreCallingIdentity(id); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 25a23fd4c72f..68034c8c4f79 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -49,9 +49,11 @@ import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; +import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; import android.view.AppTransitionAnimationSpec; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -71,6 +73,8 @@ import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; // State management of app transitions. When we are preparing for a // transition, mNextAppTransition will be the kind of transition to @@ -168,6 +172,8 @@ public class AppTransition implements Dump { // Keyed by task id. private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs = new SparseArray<>(); + private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture; + private boolean mNextAppTransitionAnimationsSpecsPending; private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec; private Rect mNextAppTransitionInsets = new Rect(); @@ -200,10 +206,16 @@ public class AppTransition implements Dump { private int mCurrentUserId = 0; private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>(); + private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor(); + private final Object mServiceLock; + private final WindowSurfacePlacer mWindowSurfacePlacer; - AppTransition(Context context, Handler h) { + AppTransition(Context context, Handler h, Object serviceLock, + WindowSurfacePlacer windowSurfacePlacer) { mContext = context; mH = h; + mServiceLock = serviceLock; + mWindowSurfacePlacer = windowSurfacePlacer; mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.linear_out_slow_in); mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, @@ -262,6 +274,7 @@ public class AppTransition implements Dump { void setReady() { mAppTransitionState = APP_STATE_READY; + fetchAppTransitionSpecsFromFuture(); } boolean isRunning() { @@ -296,6 +309,14 @@ public class AppTransition implements Dump { return mNextAppTransitionScaleUp; } + /** + * @return true if and only if we are currently fetching app transition specs from the future + * passed into {@link #overridePendingAppTransitionMultiThumbFuture} + */ + boolean isFetchingAppTransitionsSpecs() { + return mNextAppTransitionAnimationsSpecsPending; + } + private boolean prepare() { if (!isRunning()) { mAppTransitionState = APP_STATE_IDLE; @@ -1408,6 +1429,24 @@ public class AppTransition implements Dump { } } + void overridePendingAppTransitionMultiThumbFuture( + IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, + boolean scaleUp) { + if (isTransitionSet()) { + mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP + : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; + mNextAppTransitionPackage = null; + mDefaultNextAppTransitionAnimationSpec = null; + mNextAppTransitionAnimationsSpecs.clear(); + mNextAppTransitionAnimationsSpecsFuture = specsFuture; + mNextAppTransitionScaleUp = scaleUp; + postAnimationCallback(); + mNextAppTransitionCallback = callback; + } else { + postAnimationCallback(); + } + } + void overrideInPlaceAppTransition(String packageName, int anim) { if (isTransitionSet()) { mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE; @@ -1419,6 +1458,35 @@ public class AppTransition implements Dump { } } + /** + * If a future is set for the app transition specs, fetch it in another thread. + */ + private void fetchAppTransitionSpecsFromFuture() { + if (mNextAppTransitionAnimationsSpecsFuture != null) { + mNextAppTransitionAnimationsSpecsPending = true; + final IAppTransitionAnimationSpecsFuture future + = mNextAppTransitionAnimationsSpecsFuture; + mNextAppTransitionAnimationsSpecsFuture = null; + mDefaultExecutor.execute(new Runnable() { + @Override + public void run() { + AppTransitionAnimationSpec[] specs = null; + try { + specs = future.get(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to fetch app transition specs: " + e); + } + synchronized (mServiceLock) { + mNextAppTransitionAnimationsSpecsPending = false; + overridePendingAppTransitionMultiThumb(specs, mNextAppTransitionCallback, + null /* finishedCallback */, mNextAppTransitionScaleUp); + mWindowSurfacePlacer.requestTraversal(); + } + } + }); + } + } + @Override public String toString() { return "mNextAppTransition=" + appTransitionToString(mNextAppTransition); diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java index 67576baa7ef1..921d27cebe60 100644 --- a/services/core/java/com/android/server/wm/DimLayerController.java +++ b/services/core/java/com/android/server/wm/DimLayerController.java @@ -270,7 +270,14 @@ class DimLayerController { } void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) { - mState.remove(dimLayerUser); + DimLayerState state = mState.get(dimLayerUser); + if (state != null) { + // Destroy the surface, unless it's the shared fullscreen dim. + if (state.dimLayer != mSharedFullScreenDimLayer) { + state.dimLayer.destroySurface(); + } + mState.remove(dimLayerUser); + } } void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 53f8bbd9084b..8c00a570d837 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -154,6 +154,10 @@ class DisplayContent { return mDisplayMetrics; } + DockedStackDividerController getDockedDividerController() { + return mDividerControllerLocked; + } + /** * Returns true if the specified UID has access to this display. */ @@ -192,7 +196,6 @@ class DisplayContent { void updateDisplayInfo() { mDisplay.getDisplayInfo(mDisplayInfo); mDisplay.getMetrics(mDisplayMetrics); - mDividerControllerLocked.updateDisplayInfo(); for (int i = mStacks.size() - 1; i >= 0; --i) { mStacks.get(i).updateDisplayInfo(null); } @@ -373,6 +376,12 @@ class DisplayContent { */ if (isFreeformed) { mTmpRect.inset(-delta, -delta); + // Intersect with display content rect. If we have system decor (status bar/ + // navigation bar), we want to exclude that from the tap detection. + // Otherwise, if the app is partially placed under some system button (eg. + // Recents, Home), pressing that button would cause a full series of + // unwanted transfer focus/resume/pause, before we could go home. + mTmpRect.intersect(mContentRect); } mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE); } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 77ae35a0d21a..63b58b8499c0 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -16,124 +16,64 @@ package com.android.server.wm; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW; -import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; -import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA; -import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS; -import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP; -import static com.android.server.wm.TaskStack.DOCKED_BOTTOM; -import static com.android.server.wm.TaskStack.DOCKED_LEFT; -import static com.android.server.wm.TaskStack.DOCKED_RIGHT; -import static com.android.server.wm.TaskStack.DOCKED_TOP; -import static com.android.server.wm.WindowManagerService.dipToPixel; - import android.content.Context; -import android.content.res.Configuration; -import android.graphics.PixelFormat; import android.graphics.Rect; -import android.os.RemoteException; -import android.util.Slog; -import android.view.DisplayInfo; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; + +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.view.WindowManager.DOCKED_BOTTOM; +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_RIGHT; +import static android.view.WindowManager.DOCKED_TOP; /** - * Controls showing and hiding of a docked stack divider on the display. + * Keeps information about the docked stack divider. */ -public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser { - private static final String TAG = "DockedStackDivider"; - private final Context mContext; - private final int mDividerWidth; +public class DockedStackDividerController { + + private static final String TAG = "DockedStackDividerController"; + private final DisplayContent mDisplayContent; - private final int mSideMargin; - private final DimLayer mDimLayer; - private int mDisplayWidth; - private int mDisplayHeight; - private View mView; - private Rect mTmpRect = new Rect(); - private Rect mLastResizeRect = new Rect(); - private int mStartX; - private int mStartY; - private TaskStack mTaskStack; - private Rect mOriginalRect = new Rect(); - private int mDockSide; - private boolean mDimLayerVisible; + private final int mDividerWindowWidth; + private final int mDividerInsets; + private boolean mResizing; + private WindowState mWindow; + private final Rect mTmpRect = new Rect(); DockedStackDividerController(Context context, DisplayContent displayContent) { - mContext = context; mDisplayContent = displayContent; - updateDisplayInfo(); - mDividerWidth = context.getResources().getDimensionPixelSize( + mDividerWindowWidth = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); - mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics()); - mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId()); + mDividerInsets = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.docked_stack_divider_insets); } - private void addDivider(Configuration configuration) { - View view = LayoutInflater.from(mContext).inflate( - com.android.internal.R.layout.docked_stack_divider, null); - view.setOnTouchListener(this); - WindowManagerGlobal manager = WindowManagerGlobal.getInstance(); - final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE; - final int width = landscape ? mDividerWidth : MATCH_PARENT; - final int height = landscape ? MATCH_PARENT : mDividerWidth; - view.setPointerShape( - landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW); - WindowManager.LayoutParams params = new WindowManager.LayoutParams( - width, height, TYPE_DOCK_DIVIDER, - FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL - | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH, - PixelFormat.OPAQUE); - params.setTitle(TAG); - manager.addView(view, params, mDisplayContent.getDisplay(), null); - mView = view; + boolean isResizing() { + return mResizing; } - private void removeDivider() { - mView.setOnTouchListener(null); - WindowManagerGlobal manager = WindowManagerGlobal.getInstance(); - manager.removeView(mView, true /* immediate */); - mView = null; + int getContentWidth() { + return mDividerWindowWidth - 2 * mDividerInsets; } - boolean hasDivider() { - return mView != null; + void setResizing(boolean resizing) { + mResizing = resizing; } - void updateDisplayInfo() { - final DisplayInfo info = mDisplayContent.getDisplayInfo(); - mDisplayWidth = info.logicalWidth; - mDisplayHeight = info.logicalHeight; + void setWindow(WindowState window) { + mWindow = window; + reevaluateVisibility(); } - void update(Configuration configuration, boolean forceUpdate) { - if (forceUpdate && mView != null) { - removeDivider(); - } + void reevaluateVisibility() { + if (mWindow == null) return; TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID); - if (stack != null && stack.hasWindowWithFinalVisibility() && mView == null) { - addDivider(configuration); - } else if ((stack == null || !stack.isVisibleLocked()) && mView != null) { - removeDivider(); + if (stack != null && stack.isVisibleLocked()) { + mWindow.showLw(true /* doAnimation */); + } else { + mWindow.hideLw(true /* doAnimation */); } } - int getWidth() { - return mDividerWidth; - } - void positionDockedStackedDivider(Rect frame) { TaskStack stack = mDisplayContent.getDockedStackLocked(); if (stack == null) { @@ -142,265 +82,25 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa // divider was deferred to WM thread and hasn't happened yet. return; } - final @TaskStack.DockSide int side = stack.getDockSide(); + int side = stack.getDockSide(); stack.getBounds(mTmpRect); switch (side) { case DOCKED_LEFT: - frame.set(mTmpRect.right, frame.top, mTmpRect.right + frame.width(), frame.bottom); - break; - case DOCKED_TOP: - frame.set(frame.left, mTmpRect.bottom, mTmpRect.right, - mTmpRect.bottom + frame.height()); - break; - case DOCKED_RIGHT: - frame.set(mTmpRect.left - frame.width(), frame.top, mTmpRect.left, frame.bottom); - break; - case DOCKED_BOTTOM: - frame.set(frame.left, mTmpRect.top - frame.height(), frame.right, mTmpRect.top); - break; - } - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - final int action = event.getAction() & MotionEvent.ACTION_MASK; - switch (action) { - case MotionEvent.ACTION_DOWN: - // We use raw values, because getX/Y() would give us results relative to the - // dock divider bounds. - mStartX = (int) event.getRawX(); - mStartY = (int) event.getRawY(); - synchronized (mDisplayContent.mService.mWindowMap) { - mTaskStack = mDisplayContent.getDockedStackLocked(); - if (mTaskStack != null) { - mTaskStack.getBounds(mOriginalRect); - mDockSide = mTaskStack.getDockSide(); - } - } - break; - case MotionEvent.ACTION_MOVE: - if (mTaskStack != null) { - final int x = (int) event.getRawX(); - final int y = (int) event.getRawY(); - resizeStack(x, y); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - if (mTaskStack != null) { - final int x = (int) event.getRawX(); - final int y = (int) event.getRawY(); - // At most one of these will be executed, the other one will exit early. - maybeDismissTaskStack(x, y); - maybeMaximizeTaskStack(x, y); - mTaskStack = null; - } - setDimLayerVisible(false); - mDockSide = TaskStack.DOCKED_INVALID; - break; - } - return true; - } - - private void maybeMaximizeTaskStack(int x, int y) { - final int distance = distanceFromFullScreen(mDockSide, x, y); - if (distance == -1) { - Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide); - return; - } - if (distance <= mSideMargin) { - try { - mDisplayContent.mService.mActivityManager.resizeStack( - mTaskStack.mStackId, null, true); - } catch (RemoteException e) { - // This can't happen because we are in the same process. - } - } - } - - private void maybeDismissTaskStack(int x, int y) { - final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y); - if (distance == -1) { - Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide); - return; - } - if (distance <= mSideMargin) { - try { - mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId); - } catch (RemoteException e) { - // This can't happen because we are in the same process. - } - } - } - - private void updateDimLayer(int x, int y) { - final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y); - final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y); - if (dismissDistance == -1 || maximizeDistance == -1) { - Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide); - return; - } - if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) { - Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer."); - return; - } - if (dismissDistance <= mSideMargin) { - setDismissDimLayerVisible(x, y); - } else if (maximizeDistance <= mSideMargin) { - setMaximizeDimLayerVisible(x, y); - } else { - setDimLayerVisible(false); - } - } - - /** - * Provides the distance from the point to the docked side of a rectangle. - * - * @return non negative distance or -1 on error - */ - private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) { - switch (dockSide) { - case DOCKED_LEFT: - return x - bounds.left; - case DOCKED_TOP: - return y - bounds.top; - case DOCKED_RIGHT: - return bounds.right - x; - case DOCKED_BOTTOM: - return bounds.bottom - y; - } - return -1; - } - - private int distanceFromFullScreen(int dockSide, int x, int y) { - switch (dockSide) { - case DOCKED_LEFT: - return mDisplayWidth - x; - case DOCKED_TOP: - return mDisplayHeight - y; - case DOCKED_RIGHT: - return x; - case DOCKED_BOTTOM: - return y; - } - return -1; - } - - private void setDismissDimLayerVisible(int x, int y) { - mTmpRect.set(mOriginalRect); - switch (mDockSide) { - case DOCKED_LEFT: - mTmpRect.right = x; - break; - case DOCKED_TOP: - mTmpRect.bottom = y; - break; - case DOCKED_RIGHT: - mTmpRect.left = x; - break; - case DOCKED_BOTTOM: - mTmpRect.top = y; - break; - default: - Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim " - + "layer=" + mDockSide); - return; - } - mDimLayer.setBounds(mTmpRect); - setDimLayerVisible(true); - } - - private void setMaximizeDimLayerVisible(int x, int y) { - mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight); - switch (mDockSide) { - case DOCKED_LEFT: - mTmpRect.left = x; - break; - case DOCKED_TOP: - mTmpRect.top = y; - break; - case DOCKED_RIGHT: - mTmpRect.right = x; - break; - case DOCKED_BOTTOM: - mTmpRect.top = y; - break; - default: - Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim " - + "layer=" + mDockSide); - } - mDimLayer.setBounds(mTmpRect); - setDimLayerVisible(true); - } - - private void setDimLayerVisible(boolean visible) { - if (mDimLayerVisible == visible) { - return; - } - mDimLayerVisible = visible; - if (mDimLayerVisible) { - mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA, - RESIZING_HINT_DURATION_MS); - } else { - mDimLayer.hide(); - } - } - - private void resizeStack(int x, int y) { - mTmpRect.set(mOriginalRect); - final int deltaX = x - mStartX; - final int deltaY = y - mStartY; - switch (mDockSide) { - case DOCKED_LEFT: - mTmpRect.right += deltaX; + frame.set(mTmpRect.right - mDividerInsets, frame.top, + mTmpRect.right + frame.width() - mDividerInsets, frame.bottom); break; case DOCKED_TOP: - mTmpRect.bottom += deltaY; + frame.set(frame.left, mTmpRect.bottom - mDividerInsets, + mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets); break; case DOCKED_RIGHT: - mTmpRect.left += deltaX; + frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top, + mTmpRect.left + mDividerInsets, frame.bottom); break; case DOCKED_BOTTOM: - mTmpRect.top += deltaY; + frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets, + frame.right, mTmpRect.top + mDividerInsets); break; } - if (mTmpRect.equals(mLastResizeRect)) { - return; - } - mLastResizeRect.set(mTmpRect); - try { - mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect, true); - } catch (RemoteException e) { - // This can't happen because we are in the same process. - } - updateDimLayer(x, y); - } - - boolean isResizing() { - return mTaskStack != null; - } - - int getWidthAdjustment() { - return getWidth() / 2; - } - - @Override - public boolean isFullscreen() { - return false; - } - - @Override - public DisplayInfo getDisplayInfo() { - return mDisplayContent.getDisplayInfo(); - } - - @Override - public void getBounds(Rect outBounds) { - // This dim layer user doesn't need this. - } - - @Override - public String toShortString() { - return TAG; } } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index f35ea669a71d..400cc5e3d9b5 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -27,7 +27,6 @@ import android.content.ClipDescription; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; -import android.os.Binder; import android.os.IBinder; import android.os.Message; import android.os.Process; @@ -52,6 +51,7 @@ class DragState { SurfaceControl mSurfaceControl; int mFlags; IBinder mLocalWin; + int mPid; int mUid; ClipData mData; ClipDescription mDataDescription; @@ -270,8 +270,15 @@ class DragState { Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED"); } for (WindowState ws : mNotifiedWindows) { + float x = 0; + float y = 0; + if (!mDragResult && (ws.mSession.mPid == mPid)) { + // Report unconsumed drop location back to the app that started the drag. + x = mCurrentX; + y = mCurrentY; + } DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, - 0, 0, null, null, null, null, mDragResult); + x, y, null, null, null, null, mDragResult); try { ws.mClient.dispatchDragEvent(evt); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index f9994020f3c5..3c3123ff0ba3 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -193,20 +193,6 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { inputWindowHandle.frameTop = frame.top; inputWindowHandle.frameRight = frame.right; inputWindowHandle.frameBottom = frame.bottom; - if (child.mAttrs.type == TYPE_DOCK_DIVIDER) { - // We need to determine if the divider is horizontal or vertical and adjust its handle - // frame accordingly. - int adjustment = displayContent.mDividerControllerLocked.getWidthAdjustment(); - if (inputWindowHandle.frameRight - inputWindowHandle.frameLeft > - inputWindowHandle.frameTop - inputWindowHandle.frameBottom) { - // Horizontal divider. - inputWindowHandle.frameTop -= adjustment; - inputWindowHandle.frameBottom += adjustment; - } else { - inputWindowHandle.frameLeft -= adjustment; - inputWindowHandle.frameRight += adjustment; - } - } if (child.mGlobalScale != 1) { // If we are scaling the window, input coordinates need diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 5864b25781b0..6aaf3c72d4fc 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -328,6 +328,15 @@ class Task implements DimLayer.DimLayerUser { } } + /** + * Cancels any running thumbnail transitions associated with the task. + */ + void cancelTaskThumbnailTransition() { + for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { + mAppTokens.get(activityNdx).mAppAnimator.clearThumbnail(); + } + } + boolean showForAllUsers() { final int tokensCount = mAppTokens.size(); return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index bb3d6f77fc08..7c02b43604be 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -16,16 +16,6 @@ package com.android.server.wm; -import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; -import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; -import static com.android.server.wm.WindowManagerService.TAG; - -import android.annotation.IntDef; import android.app.ActivityManager.StackId; import android.content.res.Configuration; import android.graphics.Rect; @@ -39,10 +29,20 @@ import android.view.Surface; import com.android.server.EventLogTags; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.view.WindowManager.DOCKED_BOTTOM; +import static android.view.WindowManager.DOCKED_INVALID; +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_RIGHT; +import static android.view.WindowManager.DOCKED_TOP; +import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; +import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; +import static com.android.server.wm.WindowManagerService.TAG; + public class TaskStack implements DimLayer.DimLayerUser { // If the stack should be resized to fullscreen. @@ -86,21 +86,6 @@ public class TaskStack implements DimLayer.DimLayerUser { /** Detach this stack from its display when animation completes. */ boolean mDeferDetach; - static final int DOCKED_INVALID = -1; - static final int DOCKED_LEFT = 1; - static final int DOCKED_TOP = 2; - static final int DOCKED_RIGHT = 3; - static final int DOCKED_BOTTOM = 4; - - @IntDef({ - DOCKED_INVALID, - DOCKED_LEFT, - DOCKED_TOP, - DOCKED_RIGHT, - DOCKED_BOTTOM}) - @Retention(RetentionPolicy.SOURCE) - @interface DockSide {} - TaskStack(WindowManagerService service, int stackId) { mService = service; mStackId = stackId; @@ -401,7 +386,7 @@ public class TaskStack implements DimLayer.DimLayerUser { final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2, - mDisplayContent.mDividerControllerLocked.getWidth(), + mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); } @@ -435,7 +420,6 @@ public class TaskStack implements DimLayer.DimLayerUser { return; } - @DockSide final int dockedSide = dockedStack.getDockSide(); if (dockedSide == DOCKED_INVALID) { // Not sure how you got here...Only thing we can do is return current bounds. @@ -446,9 +430,10 @@ public class TaskStack implements DimLayer.DimLayerUser { mDisplayContent.getLogicalDisplayRect(mTmpRect); dockedStack.getRawBounds(mTmpRect2); - final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; + final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP + || dockedSide == DOCKED_LEFT; getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2, - mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft); + mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); } @@ -519,7 +504,7 @@ public class TaskStack implements DimLayer.DimLayerUser { final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds, - mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft); + mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); } final int count = mService.mStackIdToStack.size(); @@ -660,7 +645,6 @@ public class TaskStack implements DimLayer.DimLayerUser { /** * For docked workspace provides information which side of the screen was the dock anchored. */ - @DockSide int getDockSide() { if (mStackId != DOCKED_STACK_ID) { return DOCKED_INVALID; diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 4a9d8cbdf006..8b984f03ebd0 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -149,11 +149,11 @@ class WallpaperController { } void updateWallpaperVisibility() { - final boolean visible = isWallpaperVisible(mWallpaperTarget); final DisplayContent displayContent = mWallpaperTarget.getDisplayContent(); if (displayContent == null) { return; } + final boolean visible = isWallpaperVisible(mWallpaperTarget); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final int dw = displayInfo.logicalWidth; final int dh = displayInfo.logicalHeight; @@ -205,7 +205,7 @@ class WallpaperController { final WindowState wallpaper = token.windows.get(j); final WindowStateAnimator winAnimator = wallpaper.mWinAnimator; if (!winAnimator.mLastHidden || wasDeferred) { - winAnimator.hide(); + winAnimator.hide("hideWallpapers"); dispatchWallpaperVisibility(wallpaper, false); final DisplayContent displayContent = wallpaper.getDisplayContent(); if (displayContent != null) { @@ -470,13 +470,13 @@ class WallpaperController { } private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) { - final WindowAnimator winAnimator = mService.mAnimator; result.reset(); WindowState w = null; int windowDetachedI = -1; boolean resetTopWallpaper = false; boolean inFreeformSpace = false; + boolean replacing = false; for (int i = windows.size() - 1; i >= 0; i--) { w = windows.get(i); if ((w.mAttrs.type == TYPE_WALLPAPER)) { @@ -504,12 +504,13 @@ class WallpaperController { inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID; } + replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow); + // If the app is executing an animation because the keyguard is going away, // keep the wallpaper during the animation so it doesn't flicker out. final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayAnimation); - if (hasWallpaper && w.isOnScreen() - && (mWallpaperTarget == w || w.isDrawFinishedLw())) { + if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w); result.setWallpaperTarget(w, i); if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) { @@ -530,9 +531,12 @@ class WallpaperController { "Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w); result.setWallpaperTarget(w, windowDetachedI); } - if (result.wallpaperTarget == null && inFreeformSpace) { + if (result.wallpaperTarget == null + && (inFreeformSpace || (replacing && mWallpaperTarget != null))) { // In freeform mode we set the wallpaper as its own target, so we don't need an - // additional window to make it visible. + // additional window to make it visible. When we are replacing a window and there was + // wallpaper before replacement, we want to keep the window until the new windows fully + // appear and can determine the visibility, to avoid flickering. result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7fe523e90c7c..3205c6cca53f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -22,6 +22,7 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.StatusBarManager.DISABLE_MASK; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; +import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -117,6 +118,7 @@ import android.view.DisplayInfo; import android.view.DropPermissionHolder; import android.view.Gravity; import android.view.IApplicationToken; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; @@ -701,7 +703,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at " + newX + "," + newY); synchronized (mWindowMap) { - endDrag = completeDrop(newX, newY); + endDrag = completeDropLw(newX, newY); } } else { synchronized (mWindowMap) { @@ -715,7 +717,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at " + newX + "," + newY); synchronized (mWindowMap) { - endDrag = completeDrop(newX, newY); + endDrag = completeDropLw(newX, newY); } } break; @@ -745,11 +747,15 @@ public class WindowManagerService extends IWindowManager.Stub } } - private boolean completeDrop(float x, float y) { + private boolean completeDropLw(float x, float y) { WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y); + mDragState.mCurrentX = x; + mDragState.mCurrentY = y; + DropPermissionHolder dropPermissionHolder = null; - if ((mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 && + if (dropTargetWin != null && + (mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) { dropPermissionHolder = new DropPermissionHolder( mDragState.mData, @@ -904,7 +910,7 @@ public class WindowManagerService extends IWindowManager.Stub PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); mScreenFrozenLock.setReferenceCounted(false); - mAppTransition = new AppTransition(context, mH); + mAppTransition = new AppTransition(context, mH, mWindowMap, mWindowPlacerLocked); mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier); mActivityManager = ActivityManagerNative.getDefault(); @@ -1519,7 +1525,7 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(multidisplay): IMEs are only supported on the default display. getDefaultWindowListLocked().add(pos, win); mWindowsChanged = true; - moveInputMethodDialogsLocked(pos+1); + moveInputMethodDialogsLocked(pos + 1); return; } win.mTargetAppToken = null; @@ -1898,11 +1904,6 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } - } else if (type == TYPE_DOCK_DIVIDER) { - if (displayContent.mDividerControllerLocked.hasDivider()) { - Slog.w(TAG, "Attempted to add docked stack divider twice. Aborting."); - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } } else if (token.appWindowToken != null) { Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type); // It is not valid to use an app token with other system types; we will @@ -2001,6 +2002,10 @@ public class WindowManagerService extends IWindowManager.Stub } } + if (type == TYPE_DOCK_DIVIDER) { + getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win); + } + final WindowStateAnimator winAnimator = win.mWinAnimator; winAnimator.mEnterAnimationPending = true; winAnimator.mEnteringAnimation = true; @@ -2079,7 +2084,9 @@ public class WindowManagerService extends IWindowManager.Stub // happen, let's just simply add a window. return; } - Rect frame = replacedWindow.mFrame; + // We use the visible frame, because we want the animation to morph the window from what + // was visible to the user to the final destination of the new window. + Rect frame = replacedWindow.mVisibleFrame; // We treat this as if this activity was opening, so we can trigger the app transition // animation and piggy-back on existing transition animation infrastructure. mOpeningApps.add(atoken); @@ -3502,13 +3509,6 @@ public class WindowManagerService extends IWindowManager.Stub mLastFinishedFreezeSource = "new-config"; } mWindowPlacerLocked.performSurfacePlacement(); - if (orientationChanged) { - for (int i = mDisplayContents.size() - 1; i >= 0; i--) { - DisplayContent content = mDisplayContents.valueAt(i); - Message.obtain(mH, H.UPDATE_DOCKED_STACK_DIVIDER, H.DOCK_DIVIDER_FORCE_UPDATE, - H.UNUSED, content).sendToTarget(); - } - } } } @@ -3655,8 +3655,8 @@ public class WindowManagerService extends IWindowManager.Stub int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) { synchronized(mWindowMap) { - mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, - startY, targetWidth, targetHeight, startedCallback, scaleUp); + mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, startY, + targetWidth, targetHeight, startedCallback, scaleUp); } } @@ -3696,6 +3696,16 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void overridePendingAppTransitionMultiThumbFuture( + IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, + boolean scaleUp) { + synchronized(mWindowMap) { + mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, callback, + scaleUp); + } + } + + @Override public void endProlongedAnimations() { synchronized (mWindowMap) { for (final WindowState win : mWindowMap.values()) { @@ -4680,6 +4690,16 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void cancelTaskThumbnailTransition(int taskId) { + synchronized (mWindowMap) { + Task task = mTaskIdToTask.get(taskId); + if (task != null) { + task.cancelTaskThumbnailTransition(); + } + } + } + public void addTask(int taskId, int stackId, boolean toTop) { synchronized (mWindowMap) { if (DEBUG_STACK) Slog.i(TAG, "addTask: adding taskId=" + taskId @@ -7102,6 +7122,7 @@ public class WindowManagerService extends IWindowManager.Stub + " asbinder=" + window.asBinder()); } + final int callerPid = Binder.getCallingPid(); final int callerUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); IBinder token = null; @@ -7127,6 +7148,7 @@ public class WindowManagerService extends IWindowManager.Stub final IBinder winBinder = window.asBinder(); token = new Binder(); mDragState = new DragState(this, token, surface, flags, winBinder); + mDragState.mPid = callerPid; mDragState.mUid = callerUid; token = mDragState.mToken = new Binder(); @@ -7352,16 +7374,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int RESIZE_TASK = 44; /** - * Used to indicate in the message that the dock divider needs to be updated only if it's - * necessary. - */ - static final int DOCK_DIVIDER_NO_FORCE_UPDATE = 0; - /** - * Used to indicate in the message that the dock divider should be force-removed before - * updating, so new configuration can be applied. - */ - static final int DOCK_DIVIDER_FORCE_UPDATE = 1; - /** * Used to denote that an integer field in a message will not be used. */ public static final int UNUSED = 0; @@ -7904,12 +7916,10 @@ public class WindowManagerService extends IWindowManager.Stub } } } - break; case UPDATE_DOCKED_STACK_DIVIDER: { - DisplayContent content = (DisplayContent) msg.obj; - final boolean forceUpdate = msg.arg1 == DOCK_DIVIDER_FORCE_UPDATE; synchronized (mWindowMap) { - content.mDividerControllerLocked.update(mCurConfiguration, forceUpdate); + getDefaultDisplayContentLocked().getDockedDividerController() + .reevaluateVisibility(); } } break; @@ -8527,8 +8537,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowStateAnimator winAnimator = w.mWinAnimator; boolean layerChanged = false; int oldLayer = w.mLayer; - if (w.mBaseLayer == curBaseLayer || w.mIsImWindow - || (i > 0 && w.mIsWallpaper)) { + if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) { curLayer += WINDOW_LAYER_MULTIPLIER; w.mLayer = curLayer; } else { @@ -8852,7 +8861,7 @@ public class WindowManagerService extends IWindowManager.Stub + " pid=" + ws.mSession.mPid + " uid=" + ws.mSession.mUid); wsa.destroySurface(); - ws.mHasSurface = false; + ws.setHasSurface(false); mForceRemoves.add(ws); leakedSurface = true; } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) { @@ -8862,7 +8871,7 @@ public class WindowManagerService extends IWindowManager.Stub + " saved=" + ws.mAppToken.mHasSavedSurface); if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null); wsa.destroySurface(); - ws.mHasSurface = false; + ws.setHasSurface(false); ws.mAppToken.mHasSavedSurface = false; leakedSurface = true; } @@ -8909,7 +8918,7 @@ public class WindowManagerService extends IWindowManager.Stub if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin, "RECOVER DESTROY", null); surfaceController.destroyInTransaction(); - winAnimator.mWin.mHasSurface = false; + winAnimator.mWin.setHasSurface(false); scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken); } @@ -10102,6 +10111,26 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public int getDockedStackSide() { + synchronized (mWindowMap) { + TaskStack dockedStack = getDefaultDisplayContentLocked().getDockedStackLocked(); + return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(); + } + } + + @Override + public void setDockedStackResizing(boolean resizing) { + synchronized (mWindowMap) { + getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing); + requestTraversal(); + } + } + + boolean isDockedStackResizingLocked() { + return getDefaultDisplayContentLocked().getDockedDividerController().isResizing(); + } + static int dipToPixel(int dip, DisplayMetrics displayMetrics) { return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 307bd4f5df78..6d05921c386c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -108,6 +108,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { static final boolean BOUNDS_FOR_TOUCH = true; + static final int DRAG_RESIZE_MODE_FREEFORM = 0; + static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1; + final WindowManagerService mService; final WindowManagerPolicy mPolicy; final Context mContext; @@ -145,6 +148,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { boolean mAttachedHidden; // is our parent window hidden? boolean mWallpaperVisible; // for wallpaper, what was last vis report? boolean mDragResizing; + int mResizeMode; RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks; @@ -725,7 +729,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mVisibleFrame.set(mContentFrame); mStableFrame.set(mContentFrame); } else if (mAttrs.type == TYPE_DOCK_DIVIDER) { - mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame); + mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame); mContentFrame.set(mFrame); } else { mContentFrame.set(Math.max(mContentFrame.left, mFrame.left), @@ -1291,6 +1295,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { mConfigHasChanged = false; } + void setHasSurface(boolean hasSurface) { + mHasSurface = hasSurface; + } + private final class DeadWindowEventReceiver extends InputEventReceiver { DeadWindowEventReceiver(InputChannel inputChannel) { super(inputChannel, mService.mH.getLooper()); @@ -1429,9 +1437,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mWinAnimator + ": " + mPolicyVisibilityAfterAnim); } mPolicyVisibility = mPolicyVisibilityAfterAnim; - if (mDisplayContent != null) { - mDisplayContent.layoutNeeded = true; - } + setDisplayLayoutNeeded(); if (!mPolicyVisibility) { if (mService.mCurrentFocus == this) { if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, @@ -1838,15 +1844,15 @@ final class WindowState implements WindowManagerPolicy.WindowState { @Override public void run() { try { - mClient.resized(frame, overscanInsets, contentInsets, - visibleInsets, stableInsets, outsets, reportDraw, newConfig); + dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, + stableInsets, outsets, reportDraw, newConfig); } catch (RemoteException e) { // Not a remote call, RemoteException won't be raised. } } }); } else { - mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, + dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets, reportDraw, newConfig); } @@ -1875,6 +1881,17 @@ final class WindowState implements WindowManagerPolicy.WindowState { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets, + Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, + Configuration newConfig) throws RemoteException { + DisplayInfo displayInfo = getDisplayInfo(); + mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + boolean resizing = computeDragResizing(); + mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, + outsets, reportDraw, newConfig, inFreeformWorkspace() || !resizing + ? frame : mTmpRect); + } + public void registerFocusObserver(IWindowFocusObserver observer) { synchronized(mService.mWindowMap) { if (mFocusCallbacks == null) { @@ -1907,6 +1924,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mDragResizing != computeDragResizing(); } + int getResizeMode() { + return mResizeMode; + } + private boolean computeDragResizing() { final Task task = getTask(); if (task == null) { @@ -1921,6 +1942,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { void setDragResizing() { mDragResizing = computeDragResizing(); + mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing() + ? DRAG_RESIZE_MODE_DOCKED_DIVIDER + : DRAG_RESIZE_MODE_FREEFORM; } boolean isDragResizing() { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index dbbd4ab35638..91755ac879b0 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -29,6 +29,7 @@ import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC; import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; +import static com.android.server.wm.WindowState.*; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN; @@ -101,7 +102,7 @@ class WindowStateAnimator { */ boolean mSurfaceResized; WindowSurfaceController mSurfaceController; - WindowSurfaceController mPendingDestroySurface; + private WindowSurfaceController mPendingDestroySurface; /** * Set if the client has asked that the destroy of its surface be delayed @@ -109,7 +110,7 @@ class WindowStateAnimator { */ boolean mSurfaceDestroyDeferred; - boolean mDestroyPreservedSurfaceUponRedraw; + private boolean mDestroyPreservedSurfaceUponRedraw; float mShownAlpha = 0; float mAlpha = 0; float mLastAlpha = 0; @@ -460,9 +461,7 @@ class WindowStateAnimator { if (mSurfaceController != null && mSurfaceController.hasSurface()) { mService.mDestroySurface.add(mWin); mWin.mDestroying = true; - if (SHOW_TRANSACTIONS) WindowManagerService.logSurface( - mWin, "HIDE (finishExit)", null); - hide(); + hide("finishExit"); } mWin.mExiting = false; if (mWin.mRemoveOnExit) { @@ -472,11 +471,11 @@ class WindowStateAnimator { mWallpaperControllerLocked.hideWallpapers(mWin); } - void hide() { + void hide(String reason) { if (!mLastHidden) { //dump(); mLastHidden = true; - mSurfaceController.hideInTransaction(); + mSurfaceController.hideInTransaction(reason); } } @@ -537,8 +536,8 @@ class WindowStateAnimator { if (mDestroyPreservedSurfaceUponRedraw) { return; } - mSurfaceController.setLayer(WINDOW_FREEZE_LAYER); - + if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", null); + mSurfaceController.setLayer(mAnimLayer + 1); mDestroyPreservedSurfaceUponRedraw = true; mSurfaceDestroyDeferred = true; destroySurfaceLocked(); @@ -655,7 +654,7 @@ class WindowStateAnimator { attrs.getTitle().toString(), width, height, format, flags, this); - w.mHasSurface = true; + w.setHasSurface(true); if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) { Slog.i(TAG, " CREATE SURFACE " @@ -667,13 +666,13 @@ class WindowStateAnimator { + " / " + this); } } catch (OutOfResourcesException e) { - w.mHasSurface = false; + w.setHasSurface(false); Slog.w(TAG, "OutOfResourcesException creating surface"); mService.reclaimSomeSurfaceMemoryLocked(this, "create", true); mDrawState = NO_SURFACE; return null; } catch (Exception e) { - w.mHasSurface = false; + w.setHasSurface(false); Slog.e(TAG, "Exception creating surface", e); mDrawState = NO_SURFACE; return null; @@ -760,7 +759,14 @@ class WindowStateAnimator { + ": " + e.toString()); } - mWin.mHasSurface = false; + // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it + // needs to be cleared to match the WindowState.mHasSurface state. It is also necessary + // so it can be recreated successfully in mPendingDestroySurface case. + mWin.setHasSurface(false); + if (mSurfaceController != null) { + mSurfaceController.setShown(false); + } + mSurfaceController = null; mDrawState = NO_SURFACE; } } @@ -1063,8 +1069,10 @@ class WindowStateAnimator { } final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight); + final boolean isFreeformResizing = + w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM; final Rect clipRect = mTmpClipRect; - if (w.isDragResizing()) { + if (isFreeformResizing) { // When we're doing a drag-resizing, the surface is set up to cover full screen. // Set the clip rect to be the same size so that we don't get any scaling. clipRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); @@ -1093,7 +1101,7 @@ class WindowStateAnimator { // animation for docked window, otherwise the animating window will be suddenly cut off. if (!(mAnimator.mAnimating && w.inDockedWorkspace())) { - adjustCropToStackBounds(w, clipRect); + adjustCropToStackBounds(w, clipRect, isFreeformResizing); } w.transformFromScreenToSurfaceSpace(clipRect); @@ -1104,7 +1112,7 @@ class WindowStateAnimator { } } - private void adjustCropToStackBounds(WindowState w, Rect clipRect) { + private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) { final AppWindowToken appToken = w.mAppToken; final Task task = w.getTask(); // We don't apply the the stack bounds to the window that is being replaced, because it was @@ -1117,10 +1125,9 @@ class WindowStateAnimator { // When we resize we use the big surface approach, which means we can't trust the // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid // hardcoding it, we use surface coordinates. - final boolean isResizing = w.isDragResizing(); - final int frameX = isResizing ? (int) mSurfaceController.getX() : + final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() : w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left; - final int frameY = isResizing ? (int) mSurfaceController.getY() : + final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() : w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top; // We need to do some acrobatics with surface position, because their clip region is // relative to the inside of the surface, but the stack bounds aren't. @@ -1153,9 +1160,13 @@ class WindowStateAnimator { // so that we don't need to reallocate during the process. This also prevents // buffer drops due to size mismatch. final DisplayInfo displayInfo = w.getDisplayInfo(); - if (displayInfo != null && w.isDragResizing()) { + + // In freeform resize mode, put surface at 0/0. + if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) { left = 0; top = 0; + } + if (displayInfo != null && w.isDragResizing()) { width = displayInfo.logicalWidth; height = displayInfo.logicalHeight; } else { @@ -1194,9 +1205,9 @@ class WindowStateAnimator { mSurfaceController.setPositionInTransaction(left, top, recoveringMemory); mSurfaceResized = mSurfaceController.setSizeInTransaction(width, height, mDsDx * w.mHScale, mDtDx * w.mVScale, - mDsDy * w.mHScale, mDtDy * w.mVScale, + mDsDy * w.mHScale, mDtDy * w.mVScale, recoveringMemory); - + if (mSurfaceResized) { mAnimator.setPendingLayoutChanges(w.getDisplayId(), WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER); @@ -1226,9 +1237,9 @@ class WindowStateAnimator { if (mIsWallpaper && !mWin.mWallpaperVisible) { // Wallpaper is no longer visible and there is no wp target => hide it. - hide(); + hide("prepareSurfaceLocked"); } else if (w.mAttachedHidden || !w.isOnScreen()) { - hide(); + hide("prepareSurfaceLocked"); mWallpaperControllerLocked.hideWallpapers(w); // If we are waiting for this window to handle an @@ -1260,7 +1271,6 @@ class WindowStateAnimator { mLastDtDy = mDtDy; w.mLastHScale = w.mHScale; w.mLastVScale = w.mVScale; - if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "controller=" + mSurfaceController + "alpha=" + mShownAlpha + " layer=" + mAnimLayer diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index d09e00d2df9f..2e2be5c48972 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -95,9 +95,8 @@ class WindowSurfaceController { } } - void hideInTransaction() { - if (SHOW_TRANSACTIONS) logSurface( - "HIDE (performLayout)", null); + void hideInTransaction(String reason) { + if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null); if (mSurfaceControl != null) { mSurfaceShown = false; try { @@ -157,7 +156,7 @@ class WindowSurfaceController { mSurfaceControl.setWindowCrop(clipRect); mHiddenForCrop = false; } else { - hideInTransaction(); + hideInTransaction("setCrop"); mHiddenForCrop = true; } } catch (RuntimeException e) { @@ -365,6 +364,10 @@ class WindowSurfaceController { return mSurfaceShown; } + void setShown(boolean surfaceShown) { + mSurfaceShown = surfaceShown; + } + float getX() { return mSurfaceX; } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index a8fb77e322bb..1f49e97294cd 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -993,8 +993,7 @@ class WindowSurfacePlacer { } mService.mPolicy.finishLayoutLw(); - mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, - DOCK_DIVIDER_NO_FORCE_UPDATE, UNUSED, displayContent).sendToTarget(); + mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER); } /** @@ -1260,6 +1259,12 @@ class WindowSurfacePlacer { } } + // We also need to wait for the specs to be fetched, if needed. + if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true"); + return false; + } + // If the wallpaper is visible, we need to check it's ready too. return !mWallpaperControllerLocked.isWallpaperVisible() || mWallpaperControllerLocked.wallpaperTransitionReady(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bf7c745a546e..37e46fbe8a95 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -212,8 +212,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA); - DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_UNMUTE_MICROPHONE); - DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADJUST_VOLUME); DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SMS); DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FUN); DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SAFE_BOOT); @@ -4648,7 +4646,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mIPackageManager.updatePermissionFlagsForAllApps( PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0 /* flagValues */, userHandle.getIdentifier()); - // TODO This will not revert audio mute restrictions if they were set. b/24981972 synchronized (mUserManagerInternal.getUserRestrictionsLock()) { mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle.getIdentifier()); } @@ -4799,10 +4796,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (info.isGuest()) { throw new IllegalStateException("Cannot set a profile owner on a guest"); } - if (getProfileOwner(userHandle) != null) { + if (mOwners.hasProfileOwner(userHandle)) { throw new IllegalStateException("Trying to set the profile owner, but profile owner " + "is already set."); } + if (mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userHandle) { + throw new IllegalStateException("Trying to set the profile owner, but the user " + + "already has a device owner."); + } int callingUid = mInjector.binderGetCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { if (hasUserSetupCompleted(userHandle) && @@ -4832,6 +4833,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalStateException("Trying to set the device owner, but device owner " + "is already set."); } + if (mOwners.hasProfileOwner(userId)) { + throw new IllegalStateException("Trying to set the device owner, but the user already " + + "has a profile owner."); + } if (!mUserManager.isUserRunning(new UserHandle(userId))) { throw new IllegalStateException("User not running: " + userId); } @@ -5599,7 +5604,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) { Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); - final UserHandle user = new UserHandle(userHandle); synchronized (mUserManagerInternal.getUserRestrictionsLock()) { synchronized (this) { ActiveAdmin activeAdmin = @@ -5641,6 +5645,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public Bundle getUserRestrictions(ComponentName who) { + Preconditions.checkNotNull(who, "ComponentName is null"); + synchronized (this) { + final ActiveAdmin activeAdmin = + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + return activeAdmin.userRestrictions; + } + } + + @Override public boolean setApplicationHidden(ComponentName who, String packageName, boolean hidden) { Preconditions.checkNotNull(who, "ComponentName is null"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 12b3775d3071..ded44220c995 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -121,17 +121,20 @@ class Owners { if (!legacy.delete()) { Slog.e(TAG, "Failed to remove the legacy setting file"); } - return; - } + } else { + // No legacy file, read from the new format files. + new DeviceOwnerReadWriter().readFromFileLocked(); - // No legacy file, read from the new format files. - new DeviceOwnerReadWriter().readFromFileLocked(); - - final List<UserInfo> users = mUserManager.getUsers(); - for (UserInfo ui : users) { - new ProfileOwnerReadWriter(ui.id).readFromFileLocked(); + final List<UserInfo> users = mUserManager.getUsers(); + for (UserInfo ui : users) { + new ProfileOwnerReadWriter(ui.id).readFromFileLocked(); + } } } + if (hasDeviceOwner() && hasProfileOwner(getDeviceOwnerUserId())) { + Slog.w(TAG, String.format("User %d has both DO and PO, which is not supported", + getDeviceOwnerUserId())); + } } String getDeviceOwnerPackageName() { @@ -218,6 +221,10 @@ class Owners { return mDeviceOwner != null; } + boolean hasProfileOwner(int userId) { + return getProfileOwnerComponent(userId) != null; + } + /** * @return true if user restrictions need to be migrated for DO. */ diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 727858b77f41..36980e3c7d49 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -490,6 +490,17 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertEquals(admin1.getPackageName(), dpm.getDeviceOwner()); + // Try to set a profile owner on the same user, which should fail. + setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID); + dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM); + try { + dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM); + fail("IllegalStateException not thrown"); + } catch (IllegalStateException expected) { + assertTrue("Message was: " + expected.getMessage(), + expected.getMessage().contains("already has a device owner")); + } + // TODO Test getDeviceOwnerName() too. To do so, we need to change // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable. } @@ -509,6 +520,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setDeviceOwner(new ComponentName("a.b.c", ".def")); fail("Didn't throw IllegalArgumentException"); } catch (IllegalArgumentException expected) { + assertTrue("Message was: " + expected.getMessage(), + expected.getMessage().contains("Invalid component")); } } @@ -597,6 +610,17 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSetProfileOwner() throws Exception { setAsProfileOwner(admin1); + + // Try setting DO on the same user, which should fail. + setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID); + dpm.setActiveAdmin(admin2, /* refreshing= */ true, DpmMockContext.CALLER_USER_HANDLE); + try { + dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE); + fail("IllegalStateException not thrown"); + } catch (IllegalStateException expected) { + assertTrue("Message was: " + expected.getMessage(), + expected.getMessage().contains("already has a profile owner")); + } } public void testSetProfileOwner_failures() throws Exception { @@ -739,6 +763,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmTestUtils.newRestrictions(), dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions(), + dpm.getUserRestrictions(admin1) + ); dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS); dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); @@ -748,6 +776,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS), dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions( + UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS), + dpm.getUserRestrictions(admin1) + ); dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS); @@ -755,6 +788,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS), dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS), + dpm.getUserRestrictions(admin1) + ); dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); @@ -762,6 +799,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmTestUtils.newRestrictions(), dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions(), + dpm.getUserRestrictions(admin1) + ); // TODO Check inner calls. // TODO Make sure restrictions are written to the file. @@ -787,6 +828,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) .ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + UserManager.DISALLOW_OUTGOING_CALLS + ), + dpm.getUserRestrictions(admin1) + ); dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); @@ -798,6 +846,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) .ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions( + UserManager.DISALLOW_OUTGOING_CALLS + ), + dpm.getUserRestrictions(admin1) + ); dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS); @@ -806,6 +860,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) .ensureUserRestrictions() ); + DpmTestUtils.assertRestrictions( + DpmTestUtils.newRestrictions(), + dpm.getUserRestrictions(admin1) + ); // TODO Check inner calls. // TODO Make sure restrictions are written to the file. diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java index 9a1fe151a2dc..11d8136b9f5d 100644 --- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java +++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java @@ -100,16 +100,14 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { * SSLHandshakeException when used for a connection. */ private NetworkSecurityConfig getEmptyConfig() { - return new NetworkSecurityConfig(true, false, - new PinSet(new ArraySet<Pin>(), -1), - new ArrayList<CertificatesEntryRef>()); + return new NetworkSecurityConfig.Builder().build(); } private NetworkSecurityConfig getSystemStoreConfig() { - ArrayList<CertificatesEntryRef> defaultSource = new ArrayList<CertificatesEntryRef>(); - defaultSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false)); - return new NetworkSecurityConfig(true, false, new PinSet(new ArraySet<Pin>(), - -1), defaultSource); + return new NetworkSecurityConfig.Builder() + .addCertificatesEntryRef( + new CertificatesEntryRef(SystemCertificateSource.getInstance(), false)) + .build(); } public void testEmptyConfig() throws Exception { @@ -126,24 +124,20 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { = new ArraySet<Pair<Domain, NetworkSecurityConfig>>(); domainMap.add(new Pair<Domain, NetworkSecurityConfig>( new Domain("android.com", true), getEmptyConfig())); - ArrayList<CertificatesEntryRef> defaultSource = new ArrayList<CertificatesEntryRef>(); - defaultSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false)); - NetworkSecurityConfig defaultConfig = new NetworkSecurityConfig(true, false, - new PinSet(new ArraySet<Pin>(), -1), - defaultSource); + NetworkSecurityConfig defaultConfig = getSystemStoreConfig(); SSLContext context = getSSLContext(new TestConfigSource(domainMap, defaultConfig)); assertConnectionFails(context, "android.com", 443); assertConnectionSucceeds(context, "google.com", 443); } public void testBadPin() throws Exception { - ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>(); - systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false)); ArraySet<Pin> pins = new ArraySet<Pin>(); pins.add(new Pin("SHA-256", new byte[0])); - NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false, - new PinSet(pins, Long.MAX_VALUE), - systemSource); + NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() + .setPinSet(new PinSet(pins, Long.MAX_VALUE)) + .addCertificatesEntryRef( + new CertificatesEntryRef(SystemCertificateSource.getInstance(), false)) + .build(); ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>(); domainMap.add(new Pair<Domain, NetworkSecurityConfig>( @@ -155,13 +149,13 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { } public void testGoodPin() throws Exception { - ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>(); - systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false)); ArraySet<Pin> pins = new ArraySet<Pin>(); pins.add(new Pin("SHA-256", G2_SPKI_SHA256)); - NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false, - new PinSet(pins, Long.MAX_VALUE), - systemSource); + NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() + .setPinSet(new PinSet(pins, Long.MAX_VALUE)) + .addCertificatesEntryRef( + new CertificatesEntryRef(SystemCertificateSource.getInstance(), false)) + .build(); ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>(); domainMap.add(new Pair<Domain, NetworkSecurityConfig>( @@ -174,13 +168,13 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { public void testOverridePins() throws Exception { // Use a bad pin + granting the system CA store the ability to override pins. - ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>(); - systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), true)); ArraySet<Pin> pins = new ArraySet<Pin>(); pins.add(new Pin("SHA-256", new byte[0])); - NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false, - new PinSet(pins, Long.MAX_VALUE), - systemSource); + NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() + .setPinSet(new PinSet(pins, Long.MAX_VALUE)) + .addCertificatesEntryRef( + new CertificatesEntryRef(SystemCertificateSource.getInstance(), true)) + .build(); ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>(); domainMap.add(new Pair<Domain, NetworkSecurityConfig>( @@ -220,14 +214,33 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> { assertConnectionFails(context, "developer.android.com", 443); } + public void testConfigBuilderUsesParents() throws Exception { + // Check that a builder with a parent uses the parent's values when non is set. + NetworkSecurityConfig config = new NetworkSecurityConfig.Builder() + .setParent(NetworkSecurityConfig.getDefaultBuilder()) + .build(); + assert(!config.getTrustAnchors().isEmpty()); + } + + public void testConfigBuilderParentLoop() throws Exception { + NetworkSecurityConfig.Builder config1 = new NetworkSecurityConfig.Builder(); + NetworkSecurityConfig.Builder config2 = new NetworkSecurityConfig.Builder(); + config1.setParent(config2); + try { + config2.setParent(config1); + fail("Loop in NetworkSecurityConfig parents"); + } catch (IllegalArgumentException expected) { + } + } + public void testWithUrlConnection() throws Exception { - ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>(); - systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false)); ArraySet<Pin> pins = new ArraySet<Pin>(); pins.add(new Pin("SHA-256", G2_SPKI_SHA256)); - NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false, - new PinSet(pins, Long.MAX_VALUE), - systemSource); + NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder() + .setPinSet(new PinSet(pins, Long.MAX_VALUE)) + .addCertificatesEntryRef( + new CertificatesEntryRef(SystemCertificateSource.getInstance(), false)) + .build(); ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>(); domainMap.add(new Pair<Domain, NetworkSecurityConfig>( diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 7ee06f3a428b..3c260a83ecde 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -241,6 +241,13 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void overridePendingAppTransitionMultiThumbFuture( + IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback, + boolean scaleUp) throws RemoteException { + + } + + @Override public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, IRemoteCallback callback0, IRemoteCallback callback1, boolean scaleUp) { // TODO Auto-generated method stub @@ -515,10 +522,23 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public int getDockedStackSide() throws RemoteException { + return 0; + } + + @Override + public void setDockedStackResizing(boolean resizing) throws RemoteException { + } + + @Override public void cancelTaskWindowTransition(int taskId) { } @Override + public void cancelTaskThumbnailTransition(int taskId) { + } + + @Override public void endProlongedAnimations() { } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index c2d8d0c3c77b..3662573849a9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -48,7 +48,7 @@ public final class BridgeWindow implements IWindow { @Override public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6, - boolean b, Configuration configuration) throws RemoteException { + boolean b, Configuration configuration, Rect rect7) throws RemoteException { // pass for now. } |