Merge "Support for Bundle in AudioAttributes" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index ae343cd..eb8befa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -104,6 +104,7 @@
field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
+ field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS";
field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
diff --git a/api/system-current.txt b/api/system-current.txt
index 0cc80b1..bd9ca9c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -31504,6 +31504,51 @@
ctor public TransactionTooLargeException(java.lang.String);
}
+ public class UpdateEngine {
+ ctor public UpdateEngine();
+ method public void applyPayload(java.lang.String, long, long, java.lang.String[]) throws android.os.RemoteException;
+ method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler) throws android.os.RemoteException;
+ method public boolean bind(android.os.UpdateEngineCallback) throws android.os.RemoteException;
+ method public void cancel() throws android.os.RemoteException;
+ method public void resume() throws android.os.RemoteException;
+ method public void suspend() throws android.os.RemoteException;
+ }
+
+ public static final class UpdateEngine.ErrorCodeConstants {
+ ctor public UpdateEngine.ErrorCodeConstants();
+ field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc
+ field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9
+ field public static final int ERROR = 1; // 0x1
+ field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4
+ field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7
+ field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8
+ field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
+ field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
+ field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
+ field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
+ public static final class UpdateEngine.UpdateStatusConstants {
+ ctor public UpdateEngine.UpdateStatusConstants();
+ field public static final int ATTEMPTING_ROLLBACK = 8; // 0x8
+ field public static final int CHECKING_FOR_UPDATE = 1; // 0x1
+ field public static final int DISABLED = 9; // 0x9
+ field public static final int DOWNLOADING = 3; // 0x3
+ field public static final int FINALIZING = 5; // 0x5
+ field public static final int IDLE = 0; // 0x0
+ field public static final int REPORTING_ERROR_EVENT = 7; // 0x7
+ field public static final int UPDATED_NEED_REBOOT = 6; // 0x6
+ field public static final int UPDATE_AVAILABLE = 2; // 0x2
+ field public static final int VERIFYING = 4; // 0x4
+ }
+
+ public abstract class UpdateEngineCallback {
+ ctor public UpdateEngineCallback();
+ method public abstract void onPayloadApplicationComplete(int);
+ method public abstract void onStatusUpdate(int, float);
+ }
+
public final class UserHandle implements android.os.Parcelable {
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index c1cd275..e7f4336 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -104,6 +104,7 @@
field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
+ field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS";
field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index acc68cf..6206323 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1126,14 +1126,19 @@
}
}
+ private byte[] argToBytes(String arg) {
+ if (arg.equals("!")) {
+ return null;
+ } else {
+ return HexDump.hexStringToByteArray(arg);
+ }
+ }
+
private void runUnlockUser() throws Exception {
int userId = Integer.parseInt(nextArgRequired());
- String tokenHex = nextArg();
- byte[] token = null;
- if (tokenHex != null) {
- token = HexDump.hexStringToByteArray(tokenHex);
- }
- boolean success = mAm.unlockUser(userId, token);
+ byte[] token = argToBytes(nextArgRequired());
+ byte[] secret = argToBytes(nextArgRequired());
+ boolean success = mAm.unlockUser(userId, token, secret);
if (success) {
System.out.println("Success: user unlocked");
} else {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a3160f4..1954774 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2080,7 +2080,8 @@
data.enforceInterface(IActivityManager.descriptor);
int userId = data.readInt();
byte[] token = data.createByteArray();
- boolean result = unlockUser(userId, token);
+ byte[] secret = data.createByteArray();
+ boolean result = unlockUser(userId, token, secret);
reply.writeNoException();
reply.writeInt(result ? 1 : 0);
return true;
@@ -5571,12 +5572,13 @@
return result;
}
- public boolean unlockUser(int userId, byte[] token) throws RemoteException {
+ public boolean unlockUser(int userId, byte[] token, byte[] secret) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userId);
data.writeByteArray(token);
+ data.writeByteArray(secret);
mRemote.transact(IActivityManager.UNLOCK_USER_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6424520..04883a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5021,6 +5021,9 @@
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
+ // This cache location probably points at credential-encrypted
+ // storage which may not be accessible yet; assign it anyway instead
+ // of pointing at device-encrypted storage.
final File cacheDir = appContext.getCacheDir();
if (cacheDir != null) {
// Provide a usable directory for temporary files
@@ -5030,8 +5033,12 @@
+ "due to missing cache directory");
}
- // Use codeCacheDir to store generated/compiled graphics code and jit profiling data.
- final File codeCacheDir = appContext.getCodeCacheDir();
+ // Setup a location to store generated/compiled graphics code and
+ // JIT profiling data. Note that this data is stored in a
+ // device-encrypted storage area, so these caches must never contain
+ // user sensitive user data.
+ final Context deviceContext = appContext.createDeviceEncryptedStorageContext();
+ final File codeCacheDir = deviceContext.getCodeCacheDir();
if (codeCacheDir != null) {
setupGraphicsSupport(data.info, codeCacheDir);
setupJitProfileSupport(data.info, codeCacheDir);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 89d4931..eec503b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -58,6 +58,9 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.storage.IMountService;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
@@ -482,21 +485,20 @@
return f.delete();
}
- // Common-path handling of app data dir creation
+ /**
+ * Common-path handling of app data dir creation
+ */
private static File ensurePrivateDirExists(File file) {
if (!file.exists()) {
- if (!file.mkdirs()) {
- if (file.exists()) {
- // spurious failure; probably racing with another process for this app
- return file;
+ try {
+ Os.mkdir(file.getAbsolutePath(), 0771);
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EEXIST) {
+ // We must have raced with someone; that's okay
+ } else {
+ Log.w(TAG, "Failed to ensure " + file + ": " + e.getMessage());
}
- Log.w(TAG, "Failed to ensure directory " + file.getAbsolutePath());
- return null;
}
- FileUtils.setPermissions(
- file.getPath(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- -1, -1);
}
return file;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f5e7d78..b5ca6ee 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -426,7 +426,7 @@
// Multi-user APIs
public boolean switchUser(int userid) throws RemoteException;
public boolean startUserInBackground(int userid) throws RemoteException;
- public boolean unlockUser(int userid, byte[] token) throws RemoteException;
+ public boolean unlockUser(int userid, byte[] token, byte[] secret) throws RemoteException;
public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
public boolean isUserRunning(int userid, int flags) throws RemoteException;
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
new file mode 100644
index 0000000..80e6146
--- /dev/null
+++ b/core/java/android/os/UpdateEngine.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+import android.os.IUpdateEngine;
+import android.os.IUpdateEngineCallback;
+import android.os.RemoteException;
+
+import android.util.Log;
+
+/**
+ * UpdateEngine handles calls to the update engine which takes care of A/B OTA
+ * updates. It wraps up the update engine Binder APIs and exposes them as
+ * SystemApis, which will be called by system apps like GmsCore.
+ *
+ * The APIs defined in this class and UpdateEngineCallback class must be in
+ * sync with the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public class UpdateEngine {
+ private static final String TAG = "UpdateEngine";
+
+ private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService";
+
+ /**
+ * Error code from the update engine. Values must agree with the ones in
+ * system/update_engine/common/error_code.h.
+ */
+ @SystemApi
+ public static final class ErrorCodeConstants {
+ public static final int SUCCESS = 0;
+ public static final int ERROR = 1;
+ public static final int FILESYSTEM_COPIER_ERROR = 4;
+ public static final int POST_INSTALL_RUNNER_ERROR = 5;
+ public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6;
+ public static final int INSTALL_DEVICE_OPEN_ERROR = 7;
+ public static final int KERNEL_DEVICE_OPEN_ERROR = 8;
+ public static final int DOWNLOAD_TRANSFER_ERROR = 9;
+ public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
+ public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
+ public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+ }
+
+ /**
+ * Update status code from the update engine. Values must agree with the
+ * ones in system/update_engine/client_library/include/update_engine/update_status.h.
+ */
+ @SystemApi
+ public static final class UpdateStatusConstants {
+ public static final int IDLE = 0;
+ public static final int CHECKING_FOR_UPDATE = 1;
+ public static final int UPDATE_AVAILABLE = 2;
+ public static final int DOWNLOADING = 3;
+ public static final int VERIFYING = 4;
+ public static final int FINALIZING = 5;
+ public static final int UPDATED_NEED_REBOOT = 6;
+ public static final int REPORTING_ERROR_EVENT = 7;
+ public static final int ATTEMPTING_ROLLBACK = 8;
+ public static final int DISABLED = 9;
+ }
+
+ private IUpdateEngine mUpdateEngine;
+
+ @SystemApi
+ public UpdateEngine() {
+ mUpdateEngine = IUpdateEngine.Stub.asInterface(
+ ServiceManager.getService(UPDATE_ENGINE_SERVICE));
+ }
+
+ @SystemApi
+ public boolean bind(final UpdateEngineCallback callback, final Handler handler) throws RemoteException {
+ IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() {
+ @Override
+ public void onStatusUpdate(final int status, final float percent) {
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStatusUpdate(status, percent);
+ }
+ });
+ } else {
+ callback.onStatusUpdate(status, percent);
+ }
+ }
+
+ @Override
+ public void onPayloadApplicationComplete(final int errorCode) {
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPayloadApplicationComplete(errorCode);
+ }
+ });
+ } else {
+ callback.onPayloadApplicationComplete(errorCode);
+ }
+ }
+ };
+
+ return mUpdateEngine.bind(updateEngineCallback);
+ }
+
+ @SystemApi
+ public boolean bind(final UpdateEngineCallback callback) throws RemoteException {
+ return bind(callback, null);
+ }
+
+ @SystemApi
+ public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) throws RemoteException {
+ mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
+ }
+
+ @SystemApi
+ public void cancel() throws RemoteException {
+ mUpdateEngine.cancel();
+ }
+
+ @SystemApi
+ public void suspend() throws RemoteException {
+ mUpdateEngine.suspend();
+ }
+
+ @SystemApi
+ public void resume() throws RemoteException {
+ mUpdateEngine.resume();
+ }
+}
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
new file mode 100644
index 0000000..b3b856f
--- /dev/null
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+
+/**
+ * Callback function for UpdateEngine.
+ *
+ * The APIs defined in this class and UpdateEngine class must be in sync with
+ * the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class UpdateEngineCallback {
+
+ @SystemApi
+ public abstract void onStatusUpdate(int status, float percent);
+
+ @SystemApi
+ public abstract void onPayloadApplicationComplete(int errorCode);
+}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index dd8eb5f..fc440d2 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1233,7 +1233,8 @@
}
@Override
- public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException {
+ public void changeUserKey(int userId, int serialNumber,
+ byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
@@ -1241,6 +1242,27 @@
_data.writeInt(userId);
_data.writeInt(serialNumber);
_data.writeByteArray(token);
+ _data.writeByteArray(oldSecret);
+ _data.writeByteArray(newSecret);
+ mRemote.transact(Stub.TRANSACTION_changeUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber,
+ byte[] token, byte[] secret) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ _data.writeByteArray(token);
+ _data.writeByteArray(secret);
mRemote.transact(Stub.TRANSACTION_unlockUserKey, _data, _reply, 0);
_reply.readException();
} finally {
@@ -1448,6 +1470,8 @@
static final int TRANSACTION_mountAppFuse = IBinder.FIRST_CALL_TRANSACTION + 69;
+ static final int TRANSACTION_changeUserKey = IBinder.FIRST_CALL_TRANSACTION + 70;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -2026,12 +2050,24 @@
reply.writeNoException();
return true;
}
+ case TRANSACTION_changeUserKey: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ byte[] token = data.createByteArray();
+ byte[] oldSecret = data.createByteArray();
+ byte[] newSecret = data.createByteArray();
+ changeUserKey(userId, serialNumber, token, oldSecret, newSecret);
+ reply.writeNoException();
+ return true;
+ }
case TRANSACTION_unlockUserKey: {
data.enforceInterface(DESCRIPTOR);
int userId = data.readInt();
int serialNumber = data.readInt();
byte[] token = data.createByteArray();
- unlockUserKey(userId, serialNumber, token);
+ byte[] secret = data.createByteArray();
+ unlockUserKey(userId, serialNumber, token, secret);
reply.writeNoException();
return true;
}
@@ -2383,8 +2419,11 @@
public void createUserKey(int userId, int serialNumber, boolean ephemeral)
throws RemoteException;
public void destroyUserKey(int userId) throws RemoteException;
+ public void changeUserKey(int userId, int serialNumber,
+ byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException;
- public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
+ public void unlockUserKey(int userId, int serialNumber,
+ byte[] token, byte[] secret) throws RemoteException;
public void lockUserKey(int userId) throws RemoteException;
public boolean isUserKeyUnlocked(int userId) throws RemoteException;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b82638a..e7dfbd7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -991,9 +991,9 @@
}
/** {@hide} */
- public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
try {
- mMountService.unlockUserKey(userId, serialNumber, token);
+ mMountService.unlockUserKey(userId, serialNumber, token, secret);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 287c696..b2e8d33 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -57,9 +57,9 @@
* tag <code>fade</code>, along with the standard
* attributes of {@link android.R.styleable#Fade} and
* {@link android.R.styleable#Transition}.</p>
-
*/
public class Fade extends Visibility {
+ static final String PROPNAME_TRANSITION_ALPHA = "android:fade:transitionAlpha";
private static boolean DBG = Transition.DBG && false;
@@ -105,6 +105,13 @@
setMode(fadingMode);
}
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ transitionValues.values.put(PROPNAME_TRANSITION_ALPHA,
+ transitionValues.view.getTransitionAlpha());
+ }
+
/**
* Utility method to handle creating and running the Animator.
*/
@@ -119,7 +126,6 @@
}
final FadeAnimatorListener listener = new FadeAnimatorListener(view);
anim.addListener(listener);
- anim.addPauseListener(listener);
addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
@@ -138,18 +144,28 @@
Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
startView + ", " + view);
}
- return createAnimation(view, 0, 1);
+ float startAlpha = 0;
+ if (startValues != null) {
+ startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+ if (startAlpha == 1) {
+ startAlpha = 0;
+ }
+ }
+ return createAnimation(view, startAlpha, 1);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
TransitionValues endValues) {
- return createAnimation(view, 1, 0);
+ float startAlpha = 1;
+ if (startValues != null) {
+ startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+ }
+ return createAnimation(view, startAlpha, 0);
}
private static class FadeAnimatorListener extends AnimatorListenerAdapter {
private final View mView;
- private float mPausedAlpha = -1;
private boolean mLayerTypeChanged = false;
public FadeAnimatorListener(View view) {
@@ -171,16 +187,5 @@
mView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
-
- @Override
- public void onAnimationPause(Animator animator) {
- mPausedAlpha = mView.getTransitionAlpha();
- mView.setTransitionAlpha(1);
- }
-
- @Override
- public void onAnimationResume(Animator animator) {
- mView.setTransitionAlpha(mPausedAlpha);
- }
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1740f07..5b9930b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -166,6 +166,7 @@
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
void setAppVisibility(IBinder token, boolean visible);
+ void notifyAppStopped(IBinder token);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 127157b..f52b2907 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16582,11 +16582,12 @@
RenderNode renderNode = null;
Bitmap cache = null;
int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
- if (layerType == LAYER_TYPE_SOFTWARE
- || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) {
- // If not drawing with RenderNode, treat HW layers as SW
- layerType = LAYER_TYPE_SOFTWARE;
- buildDrawingCache(true);
+ if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
+ if (layerType != LAYER_TYPE_NONE) {
+ // If not drawing with RenderNode, treat HW layers as SW
+ layerType = LAYER_TYPE_SOFTWARE;
+ buildDrawingCache(true);
+ }
cache = getDrawingCache(true);
}
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index e31bbe9..1d242d3 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -65,6 +65,11 @@
}
@Override
+ public boolean getFreezesText() {
+ return true;
+ }
+
+ @Override
protected boolean getDefaultEditable() {
return true;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 712a04b..692b39d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4101,36 +4101,42 @@
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
- boolean save = mFreezesText;
- int start = 0;
- int end = 0;
+ final boolean freezesText = getFreezesText();
+ boolean hasSelection = false;
+ int start = -1;
+ int end = -1;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
- save = true;
+ hasSelection = true;
}
}
- if (save) {
+ if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
- // XXX Should also save the current scroll position!
- ss.selStart = start;
- ss.selEnd = end;
- if (mText instanceof Spanned) {
- Spannable sp = new SpannableStringBuilder(mText);
+ if (freezesText) {
+ if (mText instanceof Spanned) {
+ final Spannable sp = new SpannableStringBuilder(mText);
- if (mEditor != null) {
- removeMisspelledSpans(sp);
- sp.removeSpan(mEditor.mSuggestionRangeSpan);
+ if (mEditor != null) {
+ removeMisspelledSpans(sp);
+ sp.removeSpan(mEditor.mSuggestionRangeSpan);
+ }
+
+ ss.text = sp;
+ } else {
+ ss.text = mText.toString();
}
+ }
- ss.text = sp;
- } else {
- ss.text = mText.toString();
+ if (hasSelection) {
+ // XXX Should also save the current scroll position!
+ ss.selStart = start;
+ ss.selEnd = end;
}
if (isFocused() && start >= 0 && end >= 0) {
@@ -4224,7 +4230,9 @@
* position. By default this is false, not saving the text. Set to true
* if the text in the text view is not being saved somewhere else in
* persistent storage (such as in a content provider) so that if the
- * view is later thawed the user will not lose their data.
+ * view is later thawed the user will not lose their data. For
+ * {@link android.widget.EditText} it is always enabled, regardless of
+ * the value of the attribute.
*
* @param freezesText Controls whether a frozen icicle should include the
* entire text data: true to include it, false to not.
@@ -4238,7 +4246,7 @@
/**
* Return whether this text view is including its entire text contents
- * in frozen icicles.
+ * in frozen icicles. For {@link android.widget.EditText} it always returns true.
*
* @return Returns true if text is included, false if it isn't.
*
@@ -10111,8 +10119,8 @@
* {@link View#onSaveInstanceState}.
*/
public static class SavedState extends BaseSavedState {
- int selStart;
- int selEnd;
+ int selStart = -1;
+ int selEnd = -1;
CharSequence text;
boolean frozenWithFocus;
CharSequence error;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index d4ada95..b2ae835 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1706,7 +1706,9 @@
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
- addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ // Put it below the color views.
+ addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4cddb6c..1db75e6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -968,8 +968,7 @@
<!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
to record or display them to the user.
- <p>Not for use by third-party applications.
- @hide Pending API council approval -->
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
android:protectionLevel="signature|privileged" />
@@ -2884,6 +2883,18 @@
<permission android:name="android.permission.DISPATCH_PROVISIONING_MESSAGE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows the holder to read blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @hide -->
+ <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the holder to write blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @hide -->
+ <permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/values-mcc310-mnc200-tr/strings.xml b/core/res/res/values-mcc310-mnc200-tr/strings.xml
index 72da175..e104eb9 100644
--- a/core/res/res/values-mcc310-mnc200-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc200-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="9107329079910661798">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="9107329079910661798">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="2841003137832065541">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
index 30fdaa4..61bd0a1 100644
--- a/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="9107329079910661798">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="9107329079910661798">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="2841003137832065541">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc210-tr/strings.xml b/core/res/res/values-mcc310-mnc210-tr/strings.xml
index 3dfa134..1da0b1f 100644
--- a/core/res/res/values-mcc310-mnc210-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc210-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="5217754856196352581">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="5217754856196352581">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4688475512286389971">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
index 5ab2233..f890edf 100644
--- a/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="5217754856196352581">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="5217754856196352581">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4688475512286389971">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc220-tr/strings.xml b/core/res/res/values-mcc310-mnc220-tr/strings.xml
index 573982e..47aa7b7 100644
--- a/core/res/res/values-mcc310-mnc220-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc220-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="6238990105876016549">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="6238990105876016549">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="2866631708941520085">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml
index 36852ad..a846198 100644
--- a/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="6238990105876016549">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="6238990105876016549">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="2866631708941520085">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc230-tr/strings.xml b/core/res/res/values-mcc310-mnc230-tr/strings.xml
index 6f27685..a659f93 100644
--- a/core/res/res/values-mcc310-mnc230-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc230-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="9007462326786949889">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="9007462326786949889">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="6747587721329739803">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml
index a672a77..784f518 100644
--- a/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="9007462326786949889">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="9007462326786949889">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="6747587721329739803">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc240-tr/strings.xml b/core/res/res/values-mcc310-mnc240-tr/strings.xml
index 3917e99..3386699 100644
--- a/core/res/res/values-mcc310-mnc240-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc240-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="2734345662112241986">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="2734345662112241986">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5561711399459051107">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml
index e35f1a9..e5c299c 100644
--- a/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="2734345662112241986">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="2734345662112241986">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5561711399459051107">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc250-tr/strings.xml b/core/res/res/values-mcc310-mnc250-tr/strings.xml
index b44d969..a62afaf 100644
--- a/core/res/res/values-mcc310-mnc250-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc250-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="3177110876268966">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="3177110876268966">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5743977848030289234">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml
index ccb3c39..b90c30a 100644
--- a/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="3177110876268966">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="3177110876268966">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5743977848030289234">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc270-tr/strings.xml b/core/res/res/values-mcc310-mnc270-tr/strings.xml
index 2faa740..4a2fc4c 100644
--- a/core/res/res/values-mcc310-mnc270-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc270-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="6674750523418536585">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="6674750523418536585">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5880767641285399402">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml
index 7313d53..ec0e510 100644
--- a/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="6674750523418536585">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="6674750523418536585">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5880767641285399402">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc310-tr/strings.xml b/core/res/res/values-mcc310-mnc310-tr/strings.xml
index 12d2864..b0389f1 100644
--- a/core/res/res/values-mcc310-mnc310-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc310-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="1972026366984640493">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="1972026366984640493">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="1383416528714661108">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml
index 99fe393..16fa1a4 100644
--- a/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="1972026366984640493">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="1972026366984640493">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="1383416528714661108">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc490-tr/strings.xml b/core/res/res/values-mcc310-mnc490-tr/strings.xml
index 71ba5bb..2c44a4c 100644
--- a/core/res/res/values-mcc310-mnc490-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc490-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="2780619740658228275">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="2780619740658228275">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4633656294483906293">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml
index 1d39868..9348b30 100644
--- a/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="2780619740658228275">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="2780619740658228275">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4633656294483906293">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc660-tr/strings.xml b/core/res/res/values-mcc310-mnc660-tr/strings.xml
index 7e02f95..aa8bc99 100644
--- a/core/res/res/values-mcc310-mnc660-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc660-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="4027376374798357928">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="4027376374798357928">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5536938168415300276">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml
index 2a217b5..20d8703 100644
--- a/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="4027376374798357928">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="4027376374798357928">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="5536938168415300276">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc800-tr/strings.xml b/core/res/res/values-mcc310-mnc800-tr/strings.xml
index 735ce58..9e9d8e7 100644
--- a/core/res/res/values-mcc310-mnc800-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc800-tr/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="8435554129271297367">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ <item msgid="8435554129271297367">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="8993797655078232716">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml
index 0bd3c7a..ff4215d 100644
--- a/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="8435554129271297367">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+ <item msgid="8435554129271297367">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="8993797655078232716">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 482378a..1680259 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1031,7 +1031,7 @@
<string name="sim_added_title" msgid="3719670512889674693">"SIM 卡已新增"</string>
<string name="sim_added_message" msgid="7797975656153714319">"重新啟動裝置,才能使用流動網絡。"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"重新啟動"</string>
- <string name="carrier_app_dialog_message" msgid="7066156088266319533">"為了讓新的 SIM 卡正常運作,您必須先安裝並開啟流動網絡供應商提供的應用程式。"</string>
+ <string name="carrier_app_dialog_message" msgid="7066156088266319533">"為確保新的 SIM 卡正常運作,您必須先安裝並開啟流動網絡供應商提供的應用程式。"</string>
<string name="carrier_app_dialog_button" msgid="7900235513678617329">"下載應用程式"</string>
<string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"暫時不要"</string>
<string name="carrier_app_notification_title" msgid="8921767385872554621">"已插入新的 SIM 卡"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b9d8661..be8577a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4430,7 +4430,9 @@
inside of its frozen icicle in addition to meta-data such as
the current cursor position. By default this is disabled;
it can be useful when the contents of a text view is not stored
- in a persistent place such as a content provider. -->
+ in a persistent place such as a content provider. For
+ {@link android.widget.EditText} it is always enabled, regardless
+ of the value of the attribute. -->
<attr name="freezesText" format="boolean" />
<!-- If set, causes words that are longer than the view is wide
to be ellipsized instead of broken in the middle.
diff --git a/core/tests/coretests/res/layout/animator_set_squares.xml b/core/tests/coretests/res/layout/animator_set_squares.xml
index 23e6eea..6888248f 100644
--- a/core/tests/coretests/res/layout/animator_set_squares.xml
+++ b/core/tests/coretests/res/layout/animator_set_squares.xml
@@ -4,7 +4,8 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:id="@+id/container">
<View
android:layout_width="50dp"
android:layout_height="50dp"
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
new file mode 100644
index 0000000..dc60423
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.transition.Transition.TransitionListener;
+import android.transition.Transition.TransitionListenerAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.support.test.espresso.Espresso.onView;
+
+public class FadeTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+ Activity mActivity;
+ public FadeTransitionTest() {
+ super(AnimatorSetActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mActivity = getActivity();
+ }
+
+ @SmallTest
+ public void testFadeOutAndIn() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ TransitionLatch latch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ assertFalse(square1.getTransitionAlpha() == 0 || square1.getTransitionAlpha() == 1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ latch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ final float transitionAlpha = square1.getTransitionAlpha();
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " + transitionAlpha,
+ transitionAlpha > 0 && transitionAlpha < 1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ }
+
+ @SmallTest
+ public void testFadeOutInterrupt() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+ fadeOut.addListener(fadeOutValueCheck);
+ TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ Thread.sleep(100);
+
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+ fadeIn.addListener(fadeInValueCheck);
+ TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+ assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+ fadeOutValueCheck.pauseTransitionAlpha,
+ fadeOutValueCheck.pauseTransitionAlpha > 0 &&
+ fadeOutValueCheck.pauseTransitionAlpha < 1);
+
+ assertTrue(inLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ }
+
+ @SmallTest
+ public void testFadeInInterrupt() throws Throwable {
+ final View square1 = mActivity.findViewById(R.id.square1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ square1.setVisibility(View.INVISIBLE);
+ }
+ });
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+ fadeIn.addListener(fadeInValueCheck);
+ TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ Thread.sleep(100);
+
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+ fadeOut.addListener(fadeOutValueCheck);
+ TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+ assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+ fadeInValueCheck.pauseTransitionAlpha,
+ fadeInValueCheck.pauseTransitionAlpha > 0 &&
+ fadeInValueCheck.pauseTransitionAlpha < 1);
+
+ assertTrue(outLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+ }
+
+ public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+ final int visibility) throws Throwable {
+ final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+ final View view = sceneRoot.findViewById(viewId);
+ TransitionLatch latch = new TransitionLatch();
+ transition.addListener(latch);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ view.setVisibility(visibility);
+ }
+ });
+ return latch;
+ }
+
+ public static class TransitionLatch implements TransitionListener {
+ public CountDownLatch startLatch = new CountDownLatch(1);
+ public CountDownLatch endLatch = new CountDownLatch(1);
+ public CountDownLatch cancelLatch = new CountDownLatch(1);
+ public CountDownLatch pauseLatch = new CountDownLatch(1);
+ public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ endLatch.countDown();
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ cancelLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ resumeLatch.countDown();
+ }
+ }
+
+ private static class FadeValueCheck extends TransitionListenerAdapter {
+ public float startTransitionAlpha;
+ public float pauseTransitionAlpha;
+ private final View mView;
+
+ public FadeValueCheck(View view) {
+ mView = view;
+ }
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startTransitionAlpha = mView.getTransitionAlpha();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseTransitionAlpha = mView.getTransitionAlpha();
+ }
+ }
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 7b43947..6bd039c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -324,4 +324,6 @@
tests/microbench/FrameBuilderBench.cpp
endif
+LOCAL_CLANG := true # workaround gcc bug
+
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index e3a5f3e..f83e1fa 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -201,8 +201,7 @@
renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
ShadowTexture* texture = renderer.caches().dropShadowCache.get(
- op.paint, (const char*) op.glyphs,
- op.glyphCount, textShadow.radius, op.positions);
+ op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!texture) return;
@@ -277,8 +276,7 @@
bool forceFinish = (renderType == TextRenderType::Flush);
bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
- fontRenderer.renderPosText(op.paint, localOpClip,
- (const char*) op.glyphs, op.glyphCount, x, y,
+ fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y,
op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
if (mustDirtyRenderTarget) {
@@ -701,8 +699,7 @@
bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
- if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip,
- reinterpret_cast<const char*>(op.glyphs), op.glyphCount,
+ if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
op.path, op.hOffset, op.vOffset,
mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
if (mustDirtyRenderTarget) {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 3db14b5..00560d7 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -428,7 +428,7 @@
if (!glyphs || count <= 0) return;
int bytesCount = 2 * count;
- DrawOp* op = new (alloc()) DrawTextOnPathOp(refText((const char*) glyphs, bytesCount),
+ DrawOp* op = new (alloc()) DrawTextOnPathOp(refBuffer<glyph_t>(glyphs, count),
bytesCount, count, refPath(&path),
hOffset, vOffset, refPaint(&paint));
addDrawOp(op);
@@ -442,11 +442,10 @@
if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
int bytesCount = count * 2;
- const char* text = refText((const char*) glyphs, bytesCount);
positions = refBuffer<float>(positions, count * 2);
Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ DrawOp* op = new (alloc()) DrawTextOp(refBuffer<glyph_t>(glyphs, count), bytesCount, count,
x, y, positions, refPaint(&paint), totalAdvance, bounds);
addDrawOp(op);
drawTextDecorations(x, y, totalAdvance, paint);
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 06e72a0..e5711e3 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -256,10 +256,6 @@
return dstBuffer;
}
- inline char* refText(const char* text, size_t byteLength) {
- return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
- }
-
inline const SkPath* refPath(const SkPath* path) {
if (!path) return nullptr;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 92217edc..20501ba 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1229,7 +1229,7 @@
class DrawSomeTextOp : public DrawOp {
public:
- DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint)
+ DrawSomeTextOp(const glyph_t* text, int bytesCount, int count, const SkPaint* paint)
: DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
virtual void output(int level, uint32_t logFlags) const override {
@@ -1251,14 +1251,14 @@
}
protected:
- const char* mText;
+ const glyph_t* mText;
int mBytesCount;
int mCount;
};
class DrawTextOnPathOp : public DrawSomeTextOp {
public:
- DrawTextOnPathOp(const char* text, int bytesCount, int count,
+ DrawTextOnPathOp(const glyph_t* text, int bytesCount, int count,
const SkPath* path, float hOffset, float vOffset, const SkPaint* paint)
: DrawSomeTextOp(text, bytesCount, count, paint),
mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
@@ -1280,7 +1280,7 @@
class DrawTextOp : public DrawStrokableOp {
public:
- DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
+ DrawTextOp(const glyph_t* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds)
: DrawStrokableOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
@@ -1341,7 +1341,7 @@
virtual const char* name() override { return "DrawText"; }
private:
- const char* mText;
+ const glyph_t* mText;
int mBytesCount;
int mCount;
float mX;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 68bae6d..1b618c6 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -557,7 +557,7 @@
mCurrentFont = Font::create(this, paint, matrix);
}
-FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
+FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
int numGlyphs, float radius, const float* positions) {
checkInit();
@@ -577,7 +577,7 @@
mBounds = nullptr;
Rect bounds;
- mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions);
+ mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
uint32_t intRadius = Blur::convertRadiusToInt(radius);
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
@@ -609,7 +609,7 @@
// text has non-whitespace, so draw and blur to create the shadow
// NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
// TODO: don't draw pure whitespace in the first place, and avoid needing this check
- mCurrentFont->render(paint, text, numGlyphs, penX, penY,
+ mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
// Unbind any PBO we might have used
@@ -643,17 +643,17 @@
issueDrawCommand();
}
-void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs,
+void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
const SkMatrix& matrix) {
Font* font = Font::create(this, paint, matrix);
- font->precache(paint, text, numGlyphs);
+ font->precache(paint, glyphs, numGlyphs);
}
void FontRenderer::endPrecaching() {
checkTextureUpdate();
}
-bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions,
Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
if (!mCurrentFont) {
@@ -662,7 +662,7 @@
}
initRender(clip, bounds, functor);
- mCurrentFont->render(paint, text, numGlyphs, x, y, positions);
+ mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
if (forceFinish) {
finishRender();
@@ -671,7 +671,7 @@
return mDrawn;
}
-bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, const SkPath* path, float hOffset, float vOffset,
Rect* bounds, TextDrawFunctor* functor) {
if (!mCurrentFont) {
@@ -680,7 +680,7 @@
}
initRender(clip, bounds, functor);
- mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset);
+ mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
finishRender();
return mDrawn;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9994498..e10a81b 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -104,14 +104,14 @@
void setFont(const SkPaint* paint, const SkMatrix& matrix);
- void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
+ void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkMatrix& matrix);
void endPrecaching();
- bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+ bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions,
Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true);
- bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+ bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, const SkPath* path,
float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor);
@@ -125,7 +125,7 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
- DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs,
+ DropShadow renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, int numGlyphs,
float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 587be92..b7a5923 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1949,7 +1949,7 @@
}
}
-void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
+void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const glyph_t* glyphs,
int count, const float* positions,
FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.textureState().activateTexture(0);
@@ -1963,7 +1963,7 @@
// if shader-based correction is enabled
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
ShadowTexture* texture = mCaches.dropShadowCache.get(
- paint, text, count, textShadow.radius, positions);
+ paint, glyphs, count, textShadow.radius, positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!texture) return;
@@ -2084,14 +2084,14 @@
mState.setProjectionPathMask(allocator, path);
}
-void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+void OpenGLRenderer::drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
if (drawOpMode == DrawOpMode::kImmediate) {
// The checks for corner-case ignorable text and quick rejection is only done for immediate
// drawing as ops from DeferredDisplayList are already filtered for these
- if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
+ if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
quickRejectSetupScissor(bounds)) {
return;
}
@@ -2115,7 +2115,7 @@
if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
- drawTextShadow(paint, text, count, positions, fontRenderer,
+ drawTextShadow(paint, glyphs, count, positions, fontRenderer,
alpha, oldX, oldY);
}
@@ -2156,10 +2156,10 @@
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
- status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y,
+ status = fontRenderer.renderPosText(&paintCopy, clip, glyphs, count, x, y,
positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
} else {
- status = fontRenderer.renderPosText(paint, clip, text, count, x, y,
+ status = fontRenderer.renderPosText(paint, clip, glyphs, count, x, y,
positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
}
@@ -2173,9 +2173,9 @@
mDirty = true;
}
-void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
+void OpenGLRenderer::drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count,
const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
- if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
+ if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
return;
}
@@ -2198,7 +2198,7 @@
const Rect* clip = &writableSnapshot()->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
- if (fontRenderer.renderTextOnPath(paint, clip, text, count, path,
+ if (fontRenderer.renderTextOnPath(paint, clip, glyphs, count, path,
hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
mDirty = true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 84bc9b0..dacd8cc 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -191,9 +191,9 @@
void drawPath(const SkPath* path, const SkPaint* paint);
void drawLines(const float* points, int count, const SkPaint* paint);
void drawPoints(const float* points, int count, const SkPaint* paint);
- void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+ void drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path,
float hOffset, float vOffset, const SkPaint* paint);
- void drawText(const char* text, int bytesCount, int count, float x, float y,
+ void drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = DrawOpMode::kImmediate);
void drawRects(const float* rects, int count, const SkPaint* paint);
@@ -647,7 +647,7 @@
* @param x The x coordinate where the shadow will be drawn
* @param y The y coordinate where the shadow will be drawn
*/
- void drawTextShadow(const SkPaint* paint, const char* text, int count,
+ void drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count,
const float* positions, FontRenderer& fontRenderer, int alpha,
float x, float y);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3e11151..249b5b0 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -31,9 +31,6 @@
// Compile-time properties
///////////////////////////////////////////////////////////////////////////////
-// If turned on, text is interpreted as glyphs instead of UTF-16
-#define RENDER_TEXT_AS_GLYPHS 1
-
// Textures used by layers must have dimensions multiples of this number
#define LAYER_SIZE 64
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index fe4b3d75..e1f0b2a 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -37,9 +37,9 @@
hash = JenkinsHashMix(hash, flags);
hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
hash = JenkinsHashMix(hash, android::hash_type(scaleX));
- if (text) {
+ if (glyphs) {
hash = JenkinsHashMixShorts(
- hash, reinterpret_cast<const uint16_t*>(text), glyphCount);
+ hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount);
}
if (positions) {
for (uint32_t i = 0; i < glyphCount * 2; i++) {
@@ -71,11 +71,11 @@
if (lhs.scaleX < rhs.scaleX) return -1;
if (lhs.scaleX > rhs.scaleX) return +1;
- if (lhs.text != rhs.text) {
- if (!lhs.text) return -1;
- if (!rhs.text) return +1;
+ if (lhs.glyphs != rhs.glyphs) {
+ if (!lhs.glyphs) return -1;
+ if (!rhs.glyphs) return +1;
- deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t));
+ deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t));
if (deltaInt != 0) return deltaInt;
}
@@ -145,7 +145,7 @@
mCache.clear();
}
-ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs,
+ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
float radius, const float* positions) {
ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
ShadowTexture* texture = mCache.get(entry);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index cf64788..d536c40 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -35,26 +35,21 @@
struct ShadowText {
ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
- flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) {
+ flags(0), italicStyle(0.0f), scaleX(0), glyphs(nullptr), positions(nullptr) {
}
// len is the number of bytes in text
- ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText,
- const float* positions):
- glyphCount(glyphCount), radius(radius), positions(positions) {
- // TODO: Propagate this through the API, we should not cast here
- text = (const char16_t*) srcText;
-
- textSize = paint->getTextSize();
- typeface = paint->getTypeface();
-
- flags = 0;
- if (paint->isFakeBoldText()) {
- flags |= Font::kFakeBold;
- }
-
- italicStyle = paint->getTextSkewX();
- scaleX = paint->getTextScaleX();
+ ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const glyph_t* srcGlyphs,
+ const float* positions)
+ : glyphCount(glyphCount)
+ , radius(radius)
+ , textSize(paint->getTextSize())
+ , typeface(paint->getTypeface())
+ , flags(paint->isFakeBoldText() ? Font::kFakeBold : 0)
+ , italicStyle(paint->getTextSkewX())
+ , scaleX(paint->getTextScaleX())
+ , glyphs(srcGlyphs)
+ , positions(positions) {
}
~ShadowText() {
@@ -73,8 +68,8 @@
}
void copyTextLocally() {
- str.setTo((const char16_t*) text, glyphCount);
- text = str.string();
+ str.setTo(reinterpret_cast<const char16_t*>(glyphs), glyphCount);
+ glyphs = reinterpret_cast<const glyph_t*>(str.string());
if (positions != nullptr) {
positionsCopy.clear();
positionsCopy.appendArray(positions, glyphCount * 2);
@@ -89,7 +84,7 @@
uint32_t flags;
float italicStyle;
float scaleX;
- const char16_t* text;
+ const glyph_t* glyphs;
const float* positions;
// Not directly used to compute the cache key
@@ -135,7 +130,7 @@
*/
void operator()(ShadowText& text, ShadowTexture*& texture) override;
- ShadowTexture* get(const SkPaint* paint, const char* text,
+ ShadowTexture* get(const SkPaint* paint, const glyph_t* text,
int numGlyphs, float radius, const float* positions);
/**
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index dc82041..9a825fd 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -291,15 +291,15 @@
return cachedGlyph;
}
-void Font::render(const SkPaint* paint, const char *text,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr,
+ render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr,
0, 0, nullptr, positions);
}
-void Font::render(const SkPaint* paint, const char *text, int numGlyphs,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
const SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == nullptr) {
+ if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
@@ -315,7 +315,7 @@
float pathLength = SkScalarToFloat(measure.getLength());
if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2));
+ float textWidth = SkScalarToFloat(paint->measureText(glyphs, numGlyphs * 2));
float pathOffset = pathLength;
if (paint->getTextAlign() == SkPaint::kCenter_Align) {
textWidth *= 0.5f;
@@ -325,7 +325,7 @@
}
while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
+ glyph_t glyph = *(glyphs++);
if (IS_END_OF_STRING(glyph)) {
break;
@@ -345,26 +345,26 @@
}
}
-void Font::measure(const SkPaint* paint, const char* text,
+void Font::measure(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, Rect *bounds, const float* positions) {
if (bounds == nullptr) {
ALOGE("No return rectangle provided to measure text");
return;
}
bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
+ render(paint, glyphs, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
}
-void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
+void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) {
ATRACE_NAME("Precache Glyphs");
- if (numGlyphs == 0 || text == nullptr) {
+ if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
int glyphsCount = 0;
while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
+ glyph_t glyph = *(glyphs++);
// Reached the end of the string
if (IS_END_OF_STRING(glyph)) {
@@ -376,10 +376,10 @@
}
}
-void Font::render(const SkPaint* paint, const char* text,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == nullptr) {
+ if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
@@ -396,7 +396,7 @@
int glyphsCount = 0;
while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
+ glyph_t glyph = *(glyphs++);
// Reached the end of the string
if (IS_END_OF_STRING(glyph)) {
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 59518a1..e8882d9 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -82,10 +82,10 @@
~Font();
- void render(const SkPaint* paint, const char* text,
+ void render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions);
- void render(const SkPaint* paint, const char* text,
+ void render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, const SkPath* path, float hOffset, float vOffset);
const Font::FontDescription& getDescription() const {
@@ -111,13 +111,13 @@
MEASURE,
};
- void precache(const SkPaint* paint, const char* text, int numGlyphs);
+ void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs);
- void render(const SkPaint* paint, const char *text,
+ void render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
- void measure(const SkPaint* paint, const char* text,
+ void measure(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, Rect *bounds, const float* positions);
void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index 4e5debe..aa77d98 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -40,26 +40,9 @@
#define CACHE_BLOCK_ROUNDING_SIZE 4
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-
- static inline glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(cache, glyph) cache->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
+typedef uint16_t glyph_t;
+#define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
+#define IS_END_OF_STRING(glyph) false
#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5ed7aa4..3440d03 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -58,27 +58,22 @@
return layerUpdater;
}
-void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
- const SkPaint& paint, float x, float y) {
- // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
- LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
- "must use glyph encoding");
+void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text,
+ std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
+ float* outTotalAdvance, Rect* outBounds) {
+ Rect bounds;
+ float totalAdvance = 0;
SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
- float totalAdvance = 0;
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- Rect bounds;
while (*text != '\0') {
SkUnichar unichar = SkUTF8_NextUnichar(&text);
glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
autoCache.getCache()->unicharToGlyph(unichar);
// push glyph and its relative position
- glyphs.push_back(glyph);
- positions.push_back(totalAdvance);
- positions.push_back(0);
+ outGlyphs->push_back(glyph);
+ outPositions->push_back(totalAdvance);
+ outPositions->push_back(0);
// compute bounds
SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
@@ -91,6 +86,23 @@
paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
totalAdvance += skWidth;
}
+ *outBounds = bounds;
+ *outTotalAdvance = totalAdvance;
+}
+
+void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+ const SkPaint& paint, float x, float y) {
+ // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
+ LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
+ "must use glyph encoding");
+ SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+ SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
+
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ float totalAdvance;
+ Rect bounds;
+ layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
// apply alignment via x parameter (which JNI layer would have done)
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ae08142..6f23705 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -205,6 +205,10 @@
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
+ static void layoutTextUnscaled(const SkPaint& paint, const char* text,
+ std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
+ float* outTotalAdvance, Rect* outBounds);
+
static void drawTextToCanvas(TestCanvas* canvas, const char* text,
const SkPaint& paint, float x, float y);
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index c54f2c3..0d26df2 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -21,29 +21,31 @@
#include "utils/Blur.h"
#include "tests/common/TestUtils.h"
-#include <SkBlurDrawLooper.h>
#include <SkPaint.h>
using namespace android;
using namespace android::uirenderer;
RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+ SkPaint paint;
+ paint.setTextSize(20);
+
GammaFontRenderer gammaFontRenderer;
FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
- TextDropShadowCache cache(5000);
+ fontRenderer.setFont(&paint, SkMatrix::I());
+ TextDropShadowCache cache(MB(5));
cache.setFontRenderer(fontRenderer);
- SkPaint paint;
- paint.setLooper(SkBlurDrawLooper::Create((SkColor)0xFFFFFFFF,
- Blur::convertRadiusToSigma(10), 10, 10))->unref();
- std::string msg("This is a test");
- std::unique_ptr<float[]> positions(new float[msg.length()]);
- for (size_t i = 0; i < msg.length(); i++) {
- positions[i] = i * 10.0f;
- }
- fontRenderer.setFont(&paint, SkMatrix::I());
- ShadowTexture* texture = cache.get(&paint, msg.c_str(), msg.length(),
- 10.0f, positions.get());
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ float totalAdvance;
+ uirenderer::Rect bounds;
+ TestUtils::layoutTextUnscaled(paint, "This is a test",
+ &glyphs, &positions, &totalAdvance, &bounds);
+ EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized";
+
+ ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data());
+
ASSERT_TRUE(texture);
ASSERT_FALSE(texture->cleanup);
ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 3f482c8..3b49d37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -205,7 +205,7 @@
? stack.indexOfStackTask(launchTarget)
: 0;
boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
- boolean animateNavBarScrim = true;
+ boolean animateNavBarScrim = !launchState.launchedWhileDocking;
mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
// Keep track of whether we launched from the nav bar button or via alt-tab
@@ -460,13 +460,7 @@
// wait on the system to send a signal that was never queued.
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = false;
- launchState.launchedFromSearchHome = false;
- launchState.launchedFromAppWithThumbnail = false;
- launchState.launchedToTaskId = -1;
- launchState.launchedWithAltTab = false;
- launchState.launchedHasConfigurationChanged = false;
- launchState.launchedViaDragGesture = false;
+ launchState.reset();
MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 177e841..f7ebd94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -34,10 +34,22 @@
public boolean launchedReuseTaskStackViews;
public boolean launchedHasConfigurationChanged;
public boolean launchedViaDragGesture;
+ public boolean launchedWhileDocking;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
+ public void reset() {
+ launchedFromHome = false;
+ launchedFromSearchHome = false;
+ launchedFromAppWithThumbnail = false;
+ launchedToTaskId = -1;
+ launchedWithAltTab = false;
+ launchedHasConfigurationChanged = false;
+ launchedViaDragGesture = false;
+ launchedWhileDocking = false;
+ }
+
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
@@ -46,6 +58,7 @@
// Set this flag to indicate that the configuration has changed since Recents last launched
launchedHasConfigurationChanged = true;
launchedViaDragGesture = false;
+ launchedWhileDocking = false;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index dd7b7c1..5890b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -161,6 +161,7 @@
boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
boolean mReloadTasks;
+ boolean mLaunchedWhileDocking;
// Task launching
Rect mSearchBarBounds = new Rect();
@@ -270,10 +271,10 @@
}
public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
- boolean animate, boolean reloadTasks) {
+ boolean animate, boolean launchedWhileDockingTask) {
mTriggeredFromAltTab = triggeredFromAltTab;
mDraggingInRecents = draggingInRecents;
- mReloadTasks = reloadTasks;
+ mLaunchedWhileDocking = launchedWhileDockingTask;
if (mFastAltTabTrigger.hasTriggered()) {
// We are calling this from the doze trigger, so just fall through to show Recents
mFastAltTabTrigger.resetTrigger();
@@ -338,6 +339,7 @@
}
mDraggingInRecents = false;
+ mLaunchedWhileDocking = false;
mTriggeredFromAltTab = false;
try {
@@ -865,11 +867,11 @@
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
// the stacks might have changed.
- if (mReloadTasks || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
// Create a new load plan if preloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
- if (mReloadTasks || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
@@ -957,6 +959,7 @@
launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
launchState.launchedHasConfigurationChanged = false;
launchState.launchedViaDragGesture = mDraggingInRecents;
+ launchState.launchedWhileDocking = mLaunchedWhileDocking;
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index fb86214..42ebfa9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -287,13 +287,7 @@
// wait on the system to send a signal that was never queued.
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = false;
- launchState.launchedFromSearchHome = false;
- launchState.launchedFromAppWithThumbnail = false;
- launchState.launchedToTaskId = -1;
- launchState.launchedWithAltTab = false;
- launchState.launchedHasConfigurationChanged = false;
- launchState.launchedViaDragGesture = false;
+ launchState.reset();
}
@Override
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 5e113b9..d4624f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -185,7 +185,8 @@
// If we are already occluded by the app, then just set the default background scrim now.
// Otherwise, defer until the enter animation completes to animate the scrim with the
// tasks for the home animation.
- if (launchState.launchedFromAppWithThumbnail || mStack.getTaskCount() == 0) {
+ if (launchState.launchedWhileDocking || launchState.launchedFromAppWithThumbnail
+ || mStack.getTaskCount() == 0) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
mBackgroundScrim.setAlpha(0);
@@ -645,7 +646,8 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (!launchState.launchedFromAppWithThumbnail && mStack.getTaskCount() > 0) {
+ if (!launchState.launchedWhileDocking && !launchState.launchedFromAppWithThumbnail
+ && mStack.getTaskCount() > 0) {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 12e2713..36cfac8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -73,6 +73,7 @@
private int mCurrentWidth;
private int mCurrentHeight;
private AnimatorSet mAnimator;
+ private boolean mTouching;
public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -86,6 +87,9 @@
}
public void setTouching(boolean touching, boolean animate) {
+ if (touching == mTouching) {
+ return;
+ }
if (mAnimator != null) {
mAnimator.cancel();
mAnimator = null;
@@ -103,6 +107,7 @@
animateToTarget(touching ? mCircleDiameter : mWidth,
touching ? mCircleDiameter : mHeight, touching);
}
+ mTouching = touching;
}
private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 83c22b1..1bdf5a1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -100,6 +100,7 @@
private final int[] mTempInt2 = new int[2];
private boolean mMoving;
private int mTouchSlop;
+ private boolean mBackgroundLifted;
private int mDividerInsets;
private int mDisplayWidth;
@@ -210,8 +211,8 @@
mDockSide = mWindowManagerProxy.getDockSide();
initializeSnapAlgorithm();
mWindowManagerProxy.setResizing(true);
- mWindowManager.setSlippery(false);
if (touching) {
+ mWindowManager.setSlippery(false);
liftBackground();
}
return mDockSide != WindowManager.DOCKED_INVALID;
@@ -389,6 +390,9 @@
}
private void liftBackground() {
+ if (mBackgroundLifted) {
+ return;
+ }
if (isHorizontalDivision()) {
mBackground.animate().scaleY(1.4f);
} else {
@@ -407,9 +411,13 @@
.setDuration(TOUCH_ANIMATION_DURATION)
.translationZ(mTouchElevation)
.start();
+ mBackgroundLifted = true;
}
private void releaseBackground() {
+ if (!mBackgroundLifted) {
+ return;
+ }
mBackground.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
@@ -422,6 +430,7 @@
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
.translationZ(0)
.start();
+ mBackgroundLifted = false;
}
@Override
@@ -485,7 +494,9 @@
}
// Make sure shadows are updated
- mBackground.invalidate();
+ if (mBackground.getZ() > 0f) {
+ mBackground.invalidate();
+ }
mLastResizeRect.set(mDockedRect);
if (taskPosition != TASK_POSITION_SAME) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index ecba0a4..4dbb490 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -62,6 +62,10 @@
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LockSettingsStorage.CredentialHash;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
import java.util.Arrays;
import java.util.List;
@@ -510,9 +514,9 @@
}
}
- private void unlockUser(int userId, byte[] token) {
+ private void unlockUser(int userId, byte[] token, byte[] secret) {
try {
- ActivityManagerNative.getDefault().unlockUser(userId, token);
+ ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -560,6 +564,7 @@
getGateKeeperService().clearSecureUserId(userId);
mStorage.writePatternHash(null, userId);
setKeystorePassword(null, userId);
+ clearUserKeyProtection(userId);
return;
}
@@ -573,6 +578,7 @@
byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
if (enrolledHandle != null) {
mStorage.writePatternHash(enrolledHandle, userId);
+ setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
} else {
throw new RemoteException("Failed to enroll pattern");
}
@@ -588,6 +594,7 @@
getGateKeeperService().clearSecureUserId(userId);
mStorage.writePasswordHash(null, userId);
setKeystorePassword(null, userId);
+ clearUserKeyProtection(userId);
return;
}
@@ -601,6 +608,7 @@
byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
if (enrolledHandle != null) {
mStorage.writePasswordHash(enrolledHandle, userId);
+ setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
} else {
throw new RemoteException("Failed to enroll password");
}
@@ -633,6 +641,48 @@
return hash;
}
+ private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
+ throws RemoteException {
+ if (vcr == null) {
+ throw new RemoteException("Null response verifying a credential we just set");
+ }
+ if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ throw new RemoteException("Non-OK response verifying a credential we just set: "
+ + vcr.getResponseCode());
+ }
+ byte[] token = vcr.getPayload();
+ if (token == null) {
+ throw new RemoteException("Empty payload verifying a credential we just set");
+ }
+ changeUserKey(userId, token, secretFromCredential(credential));
+ }
+
+ private void clearUserKeyProtection(int userId) throws RemoteException {
+ changeUserKey(userId, null, null);
+ }
+
+ private static byte[] secretFromCredential(String credential) throws RemoteException {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ // Personalize the hash
+ byte[] personalization = "Android FBE credential hash"
+ .getBytes(StandardCharsets.UTF_8);
+ // Pad it to the block size of the hash function
+ personalization = Arrays.copyOf(personalization, 128);
+ digest.update(personalization);
+ digest.update(credential.getBytes(StandardCharsets.UTF_8));
+ return digest.digest();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
+ }
+ }
+
+ private void changeUserKey(int userId, byte[] token, byte[] secret)
+ throws RemoteException {
+ final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
+ getMountService().changeUserKey(userId, userInfo.serialNumber, token, null, secret);
+ }
+
@Override
public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
return doVerifyPattern(pattern, false, 0, userId);
@@ -742,11 +792,11 @@
if (Arrays.equals(hash, storedHash.hash)) {
unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
- // TODO: pass through a meaningful token from gatekeeper to
- // unlock credential keys; for now pass through a stub value to
- // indicate that we came from a user challenge.
- final byte[] token = String.valueOf(userId).getBytes();
- unlockUser(userId, token);
+ // Users with legacy credentials don't have credential-backed
+ // FBE keys, so just pass through a fake token/secret
+ Slog.i(TAG, "Unlocking user with fake token: " + userId);
+ final byte[] fakeToken = String.valueOf(userId).getBytes();
+ unlockUser(userId, fakeToken, fakeToken);
// migrate credential to GateKeeper
credentialUtil.setCredential(credential, null, userId);
@@ -786,11 +836,9 @@
// credential has matched
unlockKeystore(credential, userId);
- // TODO: pass through a meaningful token from gatekeeper to
- // unlock credential keys; for now pass through a stub value to
- // indicate that we came from a user challenge.
- final byte[] token = String.valueOf(userId).getBytes();
- unlockUser(userId, token);
+ Slog.i(TAG, "Unlocking user " + userId +
+ " with token length " + response.getPayload().length);
+ unlockUser(userId, response.getPayload(), secretFromCredential(credential));
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 5120e1b..cbd477a 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2742,8 +2742,30 @@
}
}
+ private SensitiveArg encodeBytes(byte[] bytes) {
+ if (ArrayUtils.isEmpty(bytes)) {
+ return new SensitiveArg("!");
+ } else {
+ return new SensitiveArg(HexDump.toHexString(bytes));
+ }
+ }
+
@Override
- public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ public void changeUserKey(int userId, int serialNumber,
+ byte[] token, byte[] oldSecret, byte[] newSecret) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "change_user_key", userId, serialNumber,
+ encodeBytes(token), encodeBytes(oldSecret), encodeBytes(newSecret));
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
@@ -2753,16 +2775,9 @@
throw new IllegalStateException("Token required to unlock secure user " + userId);
}
- final String encodedToken;
- if (ArrayUtils.isEmpty(token)) {
- encodedToken = "!";
- } else {
- encodedToken = HexDump.toHexString(token);
- }
-
try {
mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
- new SensitiveArg(encodedToken));
+ encodeBytes(token), encodeBytes(secret));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5125133..9dae740 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20340,8 +20340,8 @@
}
@Override
- public boolean unlockUser(int userId, byte[] token) {
- return mUserController.unlockUser(userId, token);
+ public boolean unlockUser(int userId, byte[] token, byte[] secret) {
+ return mUserController.unlockUser(userId, token, secret);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c352fc8..0bccffa 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1097,6 +1097,9 @@
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
r.stopped = true;
r.state = ActivityState.STOPPED;
+
+ mWindowManager.notifyAppStopped(r.appToken);
+
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
}
@@ -1561,7 +1564,7 @@
resumeNextActivity = false;
}
} else {
- makeVisible(starting, r);
+ makeVisibleIfNeeded(starting, r);
}
// Aggregate current change flags.
configChanges |= r.configChangeFlags;
@@ -1719,28 +1722,30 @@
return behindFullscreenActivity;
}
- private void makeVisible(ActivityRecord starting, ActivityRecord r) {
+ private void makeVisibleIfNeeded(ActivityRecord starting, ActivityRecord r) {
+
// This activity is not currently visible, but is running. Tell it to become visible.
- r.visible = true;
- if (r.state != ActivityState.RESUMED && r != starting) {
- // If this activity is paused, tell it to now show its window.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Making visible and scheduling visibility: " + r);
- try {
- if (mTranslucentActivityWaiting != null) {
- r.updateOptionsLocked(r.returningOptions);
- mUndrawnActivitiesBelowTopTranslucent.add(r);
- }
- setVisible(r, true);
- r.sleeping = false;
- r.app.pendingUiClean = true;
- r.app.thread.scheduleWindowVisibility(r.appToken, true);
- r.stopFreezingScreenLocked(false);
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
+ if (r.state == ActivityState.RESUMED || r == starting) {
+ return;
+ }
+
+ // If this activity is paused, tell it to now show its window.
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+ "Making visible and scheduling visibility: " + r);
+ try {
+ if (mTranslucentActivityWaiting != null) {
+ r.updateOptionsLocked(r.returningOptions);
+ mUndrawnActivitiesBelowTopTranslucent.add(r);
}
+ setVisible(r, true);
+ r.sleeping = false;
+ r.app.pendingUiClean = true;
+ r.app.thread.scheduleWindowVisibility(r.appToken, true);
+ r.stopFreezingScreenLocked(false);
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f53e71a..0beef53 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1932,7 +1932,7 @@
private void ensureConfigurationAndResume(ActivityStack stack, ActivityRecord r,
boolean preserveWindows) {
- if (r == null) {
+ if (r == null || !r.visible) {
return;
}
final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2f63b2d3..a355fa4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -783,7 +783,7 @@
return result;
}
- boolean unlockUser(final int userId, byte[] token) {
+ boolean unlockUser(final int userId, byte[] token, byte[] secret) {
if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: unlockUser() from pid="
@@ -796,7 +796,7 @@
final long binderToken = Binder.clearCallingIdentity();
try {
- return unlockUserCleared(userId, token);
+ return unlockUserCleared(userId, token, secret);
} finally {
Binder.restoreCallingIdentity(binderToken);
}
@@ -810,10 +810,10 @@
*/
boolean maybeUnlockUser(final int userId) {
// Try unlocking storage using empty token
- return unlockUserCleared(userId, null);
+ return unlockUserCleared(userId, null, null);
}
- boolean unlockUserCleared(final int userId, byte[] token) {
+ boolean unlockUserCleared(final int userId, byte[] token, byte[] secret) {
synchronized (mService) {
// Bail if already running unlocked
final UserState uss = mStartedUsers.get(userId);
@@ -824,7 +824,7 @@
final UserInfo userInfo = getUserInfo(userId);
final IMountService mountService = getMountService();
try {
- mountService.unlockUserKey(userId, userInfo.serialNumber, token);
+ mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret);
} catch (RemoteException | RuntimeException e) {
Slog.w(TAG, "Failed to unlock: " + e.getMessage());
return false;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fcb777b..8fa5d24 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2359,7 +2359,7 @@
}
final ApplicationInfo ai = pkg.pkg.applicationInfo;
- final String dataPath = new File(ai.dataDir).getCanonicalPath();
+ final String dataPath = ai.dataDir;
final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
final int[] gids = pkg.getPermissionsState().computeGids(userIds);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7ec945d..93b1d62 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -127,6 +127,8 @@
boolean mAlwaysFocusable;
+ boolean mAppStopped;
+
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
@@ -311,6 +313,47 @@
}
}
+ // Here we destroy surfaces which have been marked as eligible by the animator, taking care
+ // to ensure the client has finished with them. If the client could still be using them
+ // we will skip destruction and try again when the client has stopped.
+ void destroySurfaces() {
+ final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
+ final DisplayContentList displayList = new DisplayContentList();
+ for (int i = allWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = allWindows.get(i);
+ if (!win.mDestroying) {
+ continue;
+ }
+
+ if (!mAppStopped && !win.mClientRemoveRequested) {
+ return;
+ }
+
+ win.destroyOrSaveSurface();
+ if (win.mRemoveOnExit) {
+ win.mExiting = false;
+ service.removeWindowInnerLocked(win);
+ }
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent != null && !displayList.contains(displayContent)) {
+ displayList.add(displayContent);
+ }
+ win.mDestroying = false;
+ }
+ for (int i = 0; i < displayList.size(); i++) {
+ final DisplayContent displayContent = displayList.get(i);
+ service.mLayersController.assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
+ // The application has stopped, so destroy any surfaces which were keeping alive
+ // in case they were still being used.
+ void notifyAppStopped() {
+ mAppStopped = true;
+ destroySurfaces();
+ }
+
/**
* Checks whether we should save surfaces for this app.
*
@@ -513,6 +556,9 @@
mFrozenBounds.remove();
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
+ if (!win.mHasSurface) {
+ continue;
+ }
win.mLayoutNeeded = true;
win.setDisplayLayoutNeeded();
if (!service.mResizingWindows.contains(win)) {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 1bfdcce..0678ca2 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -22,7 +22,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -41,8 +40,9 @@
*/
public class BoundsAnimationController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BoundsAnimationController" : TAG_WM;
+ private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
- // Only acccessed on UI thread.
+ // Only accessed on UI thread.
private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
private final class BoundsAnimator extends ValueAnimator
@@ -52,14 +52,22 @@
private final Rect mTo;
private final Rect mTmpRect;
private final boolean mMoveToFullScreen;
+ // True if this this animation was cancelled and will be replaced the another animation from
+ // the same {@link #AnimateBoundsUser} target.
+ private boolean mWillReplace;
+ // True to true if this animation replaced a previous animation of the same
+ // {@link #AnimateBoundsUser} target.
+ private final boolean mReplacement;
- BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen) {
+ BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to,
+ boolean moveToFullScreen, boolean replacement) {
super();
mTarget = target;
mFrom = from;
mTo = to;
mTmpRect = new Rect();
mMoveToFullScreen = moveToFullScreen;
+ mReplacement = replacement;
addUpdateListener(this);
addListener(this);
}
@@ -68,10 +76,10 @@
public void onAnimationUpdate(ValueAnimator animation) {
final float value = (Float) animation.getAnimatedValue();
final float remains = 1 - value;
- mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value);
- mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value);
- mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value);
- mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value);
+ mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
+ mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
+ mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
+ mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
if (DEBUG_ANIM) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + ", mBounds="
+ mTmpRect + ", from=" + mFrom + ", mTo=" + mTo + ", value=" + value
+ ", remains=" + remains);
@@ -85,13 +93,15 @@
@Override
public void onAnimationStart(Animator animation) {
-
+ if (!mReplacement) {
+ mTarget.onAnimationStart();
+ }
}
@Override
public void onAnimationEnd(Animator animation) {
finishAnimation();
- if (mMoveToFullScreen) {
+ if (mMoveToFullScreen && !mWillReplace) {
mTarget.moveToFullscreen();
}
}
@@ -101,8 +111,16 @@
finishAnimation();
}
+ @Override
+ public void cancel() {
+ mWillReplace = true;
+ super.cancel();
+ }
+
private void finishAnimation() {
- mTarget.finishBoundsAnimation();
+ if (!mWillReplace) {
+ mTarget.onAnimationEnd();
+ }
removeListener(this);
removeUpdateListener(this);
mRunningAnimations.remove(mTarget);
@@ -126,11 +144,13 @@
*/
boolean setSize(Rect bounds);
+ void onAnimationStart();
+
/**
- * Callback for the target to inform it that the animation is finished, so it can do some
+ * Callback for the target to inform it that the animation has ended, so it can do some
* necessary cleanup.
*/
- void finishBoundsAnimation();
+ void onAnimationEnd();
void moveToFullscreen();
@@ -146,13 +166,15 @@
}
final BoundsAnimator existing = mRunningAnimations.get(target);
- if (existing != null) {
+ final boolean replacing = existing != null;
+ if (replacing) {
existing.cancel();
}
- BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen);
+ final BoundsAnimator animator =
+ new BoundsAnimator(target, from, to, moveToFullscreen, replacing);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
- animator.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ animator.setDuration(DEFAULT_APP_TRANSITION_DURATION * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
animator.setInterpolator(new LinearInterpolator());
animator.start();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a06d3fc..4167ac4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -540,7 +540,7 @@
}
boolean isDragResizing() {
- return mDragResizing;
+ return mDragResizing || (mStack != null && mStack.isDragResizing());
}
void updateDisplayInfo(final DisplayContent displayContent) {
@@ -584,7 +584,7 @@
final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState win = windows.get(winNdx);
- if (!resizingWindows.contains(win)) {
+ if (win.mHasSurface && !resizingWindows.contains(win)) {
if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
resizingWindows.add(win);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 40ca1c5..a8b72892 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -99,6 +99,9 @@
boolean mDeferDetach;
private boolean mUpdateBoundsAfterRotation = false;
+ // Whether the stack and all its tasks is currently being drag-resized
+ private boolean mDragResizing;
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -911,6 +914,10 @@
return false;
}
+ boolean isDragResizing() {
+ return mDragResizing;
+ }
+
@Override // AnimatesBounds
public boolean setSize(Rect bounds) {
synchronized (mService.mWindowMap) {
@@ -926,16 +933,17 @@
}
@Override // AnimatesBounds
- public void finishBoundsAnimation() {
+ public void onAnimationStart() {
synchronized (mService.mWindowMap) {
- if (mTasks.isEmpty()) {
- return;
- }
- final Task task = mTasks.get(mTasks.size() - 1);
- if (task != null) {
- task.setDragResizing(false);
- mService.requestTraversal();
- }
+ mDragResizing = true;
+ }
+ }
+
+ @Override // AnimatesBounds
+ public void onAnimationEnd() {
+ synchronized (mService.mWindowMap) {
+ mDragResizing = false;
+ mService.requestTraversal();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ae6c89a..1b041cb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2154,6 +2154,14 @@
if (win == null) {
return;
}
+ // We set this here instead of removeWindowLocked because we only want it to be
+ // true when the client has requested we remove the window. In other remove
+ // cases, we have to wait for activity stop to safely remove the window (as the
+ // client may still be using the surface). In this case though, the client has
+ // just dismissed a window (for example a Dialog) and activity stop isn't
+ // necessarily imminent, so we need to know not to wait for it after our
+ // hanimation (if applicable) finishes.
+ win.mClientRemoveRequested = true;
removeWindowLocked(win);
}
}
@@ -4188,6 +4196,24 @@
}
@Override
+ public void notifyAppStopped(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "notifyAppStopped()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ }
+
+ synchronized(mWindowMap) {
+ final AppWindowToken wtoken;
+ wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
+ return;
+ }
+ wtoken.notifyAppStopped();
+ }
+ }
+
+ @Override
public void setAppVisibility(IBinder token, boolean visible) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppVisibility()")) {
@@ -4210,6 +4236,7 @@
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
+ wtoken.mAppStopped = false;
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
@@ -10350,13 +10377,6 @@
Slog.w(TAG, "animateResizePinnedStack: stackId " + PINNED_STACK_ID + " not found.");
return;
}
- final ArrayList<Task> tasks = stack.getTasks();
- if (tasks.isEmpty()) {
- Slog.w(TAG, "animateResizePinnedStack: pinned stack doesn't have any tasks.");
- return;
- }
- final Task task = tasks.get(tasks.size() - 1);
- task.setDragResizing(true);
final Rect originalBounds = new Rect();
stack.getBounds(originalBounds);
UiThread.getHandler().post(new Runnable() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 880514c..3430b34 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -386,6 +386,13 @@
boolean mRemoved;
/**
+ * Has the client requested we remove the window? In this case we know
+ * that we can dispose of it when we wish without further synchronization
+ * with the client
+ */
+ boolean mClientRemoveRequested;
+
+ /**
* Temp for keeping track of windows that have been removed when
* rebuilding window list.
*/
@@ -2145,7 +2152,7 @@
// background.
return (mDisplayContent.mDividerControllerLocked.isResizing()
|| mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
- !task.inFreeformWorkspace();
+ !task.inFreeformWorkspace() && isVisibleLw();
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c7c9cbf..0201296 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -471,16 +471,31 @@
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Exit animation finished in " + this
+ ": remove=" + mWin.mRemoveOnExit);
- if (hasSurface()) {
- mService.mDestroySurface.add(mWin);
- mWin.mDestroying = true;
- hide("finishExit");
+
+
+ mWin.mDestroying = true;
+
+ // If we have an app token, we ask it to destroy the surface for us,
+ // so that it can take care to ensure the activity has actually stopped
+ // and the surface is not still in use. Otherwise we add the service to
+ // mDestroySurface and allow it to be processed in our next transaction.
+ if (mWin.mAppToken != null) {
+ if (hasSurface()) {
+ hide("finishExit");
+ }
+ mWin.mAppToken.destroySurfaces();
+ } else {
+ if (hasSurface()) {
+ mService.mDestroySurface.add(mWin);
+ hide("finishExit");
+ }
+ mWin.mExiting = false;
+ if (mWin.mRemoveOnExit) {
+ mService.mPendingRemove.add(mWin);
+ mWin.mRemoveOnExit = false;
+ }
}
- mWin.mExiting = false;
- if (mWin.mRemoveOnExit) {
- mService.mPendingRemove.add(mWin);
- mWin.mRemoveOnExit = false;
- }
+
mWallpaperControllerLocked.hideWallpapers(mWin);
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2560c31..8d1b124 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -348,6 +348,11 @@
}
@Override
+ public void notifyAppStopped(IBinder token) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}