Merge "Custom LPP AGPS config for Rogers" into nyc-dr1-dev
am: e13df588a4
Change-Id: I461a69d477f540424b40ed76cba11bfaabbcabb8
diff --git a/api/test-current.txt b/api/test-current.txt
index 3b5c223..74e65d9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3255,6 +3255,7 @@
method public java.lang.Object getAnimatedValue(java.lang.String);
method public long getCurrentPlayTime();
method public long getDuration();
+ method public static float getDurationScale();
method public static long getFrameDelay();
method public int getRepeatCount();
method public int getRepeatMode();
@@ -3272,6 +3273,7 @@
method public void setCurrentFraction(float);
method public void setCurrentPlayTime(long);
method public android.animation.ValueAnimator setDuration(long);
+ method public static void setDurationScale(float);
method public void setEvaluator(android.animation.TypeEvaluator);
method public void setFloatValues(float...);
method public static void setFrameDelay(long);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 7465ed9..ed08a70 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.GET_ACCOUNTS;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.Size;
@@ -28,6 +29,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.res.Resources;
import android.database.SQLException;
import android.os.Build;
@@ -265,6 +267,15 @@
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
+ /**
+ * Token for the special case where a UID has access only to an account
+ * but no authenticator specific auth tokens.
+ *
+ * @hide
+ */
+ public static final String ACCOUNT_ACCESS_TOKEN =
+ "com.android.abbfd278-af8b-415d-af8b-7571d5dab133";
+
private final Context mContext;
private final IAccountManager mService;
private final Handler mMainHandler;
@@ -2960,4 +2971,49 @@
}
}.start();
}
+
+ /**
+ * Gets whether a given package under a user has access to an account.
+ * Can be called only from the system UID.
+ *
+ * @param account The account for which to check.
+ * @param packageName The package for which to check.
+ * @param userHandle The user for which to check.
+ * @return True if the package can access the account.
+ *
+ * @hide
+ */
+ public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
+ @NonNull UserHandle userHandle) {
+ try {
+ return mService.hasAccountAccess(account, packageName, userHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates an intent to request access to a given account for a UID.
+ * The returned intent should be stated for a result where {@link
+ * Activity#RESULT_OK} result means access was granted whereas {@link
+ * Activity#RESULT_CANCELED} result means access wasn't granted. Can
+ * be called only from the system UID.
+ *
+ * @param account The account for which to request.
+ * @param packageName The package name which to request.
+ * @param userHandle The user for which to request.
+ * @return The intent to request account access or null if the package
+ * doesn't exist.
+ *
+ * @hide
+ */
+ public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
+ @NonNull String packageName, @NonNull UserHandle userHandle) {
+ try {
+ return mService.createRequestAccountAccessIntentSenderAsUser(account, packageName,
+ userHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/accounts/AccountManagerInternal.java b/core/java/android/accounts/AccountManagerInternal.java
new file mode 100644
index 0000000..d777643
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerInternal.java
@@ -0,0 +1,44 @@
+/*
+ * 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.accounts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.RemoteCallback;
+
+/**
+ * Account manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AccountManagerInternal {
+
+ /**
+ * Requests that a given package is given access to an account.
+ * The provided callback will be invoked with a {@link android.os.Bundle}
+ * containing the result which will be a boolean value mapped to the
+ * {@link AccountManager#KEY_BOOLEAN_RESULT} key.
+ *
+ * @param account The account for which to request.
+ * @param packageName The package name for which to request.
+ * @param userId Concrete user id for which to request.
+ * @param callback A callback for receiving the result.
+ */
+ public abstract void requestAccountAccess(@NonNull Account account,
+ @NonNull String packageName, @IntRange(from = 0) int userId,
+ @NonNull RemoteCallback callback);
+}
diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
index 12b2b9c..8d0ce58 100644
--- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -35,12 +35,10 @@
*/
public class GrantCredentialsPermissionActivity extends Activity implements View.OnClickListener {
public static final String EXTRAS_ACCOUNT = "account";
- public static final String EXTRAS_AUTH_TOKEN_LABEL = "authTokenLabel";
public static final String EXTRAS_AUTH_TOKEN_TYPE = "authTokenType";
public static final String EXTRAS_RESPONSE = "response";
- public static final String EXTRAS_ACCOUNT_TYPE_LABEL = "accountTypeLabel";
- public static final String EXTRAS_PACKAGES = "application";
public static final String EXTRAS_REQUESTING_UID = "uid";
+
private Account mAccount;
private String mAuthTokenType;
private int mUid;
@@ -109,7 +107,11 @@
}
}
};
- AccountManager.get(this).getAuthTokenLabel(mAccount.type, mAuthTokenType, callback, null);
+
+ if (!AccountManager.ACCOUNT_ACCESS_TOKEN.equals(mAuthTokenType)) {
+ AccountManager.get(this).getAuthTokenLabel(mAccount.type,
+ mAuthTokenType, callback, null);
+ }
findViewById(R.id.allow_button).setOnClickListener(this);
findViewById(R.id.deny_button).setOnClickListener(this);
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 7199288..56a6488 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -19,8 +19,10 @@
import android.accounts.IAccountManagerResponse;
import android.accounts.Account;
import android.accounts.AuthenticatorDescription;
+import android.content.IntentSender;
import android.os.Bundle;
-
+import android.os.RemoteCallback;
+import android.os.UserHandle;
/**
* Central application service that provides account management.
@@ -102,4 +104,10 @@
/* Check if credentials update is suggested */
void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account,
String statusToken);
+
+ /* Check if the package in a user can access an account */
+ boolean hasAccountAccess(in Account account, String packageName, in UserHandle userHandle);
+ /* Crate an intent to request account access for package and a given user id */
+ IntentSender createRequestAccountAccessIntentSenderAsUser(in Account account,
+ String packageName, in UserHandle userHandle);
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e3f8fa4..7e16e3e 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -18,6 +18,7 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.TestApi;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
@@ -261,6 +262,7 @@
/**
* @hide
*/
+ @TestApi
public static void setDurationScale(float durationScale) {
sDurationScale = durationScale;
}
@@ -268,6 +270,7 @@
/**
* @hide
*/
+ @TestApi
public static float getDurationScale() {
return sDurationScale;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2a12ac8..b8c133e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2219,6 +2219,7 @@
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
* @throws IllegalStateException if the calling user is locked or has a managed profile.
+ * @throws IllegalArgumentException if the password does not meet system requirements.
*/
public boolean resetPassword(String password, int flags) {
throwIfParentInstance("resetPassword");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3f18ea9..f908abc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2761,8 +2761,10 @@
* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
* handling management of network connections.
* <dt> {@link #WIFI_SERVICE} ("wifi")
- * <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of
- * Wi-Fi connectivity.
+ * <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi
+ * connectivity. On releases before NYC, it should only be obtained from an application
+ * context, and not from any other derived context to avoid memory leaks within the calling
+ * process.
* <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
* <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
* Wi-Fi Direct connectivity.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4d9db98..1cf23ae 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4314,6 +4314,14 @@
public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100;
/**
+ * Internal flag used to indicate ephemeral applications should not be
+ * considered when resolving the intent.
+ *
+ * @hide
+ */
+ public static final int FLAG_IGNORE_EPHEMERAL = 0x00000200;
+
+ /**
* If set, the new activity is not kept in the history stack. As soon as
* the user navigates away from it, the activity is finished. This may also
* be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 8a16ac9..6ef7fd2 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.Nullable;
import android.text.TextUtils;
import android.os.Parcelable;
import android.os.Parcel;
@@ -33,6 +34,7 @@
private final boolean isAlwaysSyncable;
private final boolean allowParallelSyncs;
private final String settingsActivity;
+ private final String packageName;
public SyncAdapterType(String authority, String accountType, boolean userVisible,
boolean supportsUploading) {
@@ -50,6 +52,7 @@
this.allowParallelSyncs = false;
this.settingsActivity = null;
this.isKey = false;
+ this.packageName = null;
}
/** @hide */
@@ -57,7 +60,8 @@
boolean supportsUploading,
boolean isAlwaysSyncable,
boolean allowParallelSyncs,
- String settingsActivity) {
+ String settingsActivity,
+ String packageName) {
if (TextUtils.isEmpty(authority)) {
throw new IllegalArgumentException("the authority must not be empty: " + authority);
}
@@ -72,6 +76,7 @@
this.allowParallelSyncs = allowParallelSyncs;
this.settingsActivity = settingsActivity;
this.isKey = false;
+ this.packageName = packageName;
}
private SyncAdapterType(String authority, String accountType) {
@@ -89,6 +94,7 @@
this.allowParallelSyncs = false;
this.settingsActivity = null;
this.isKey = true;
+ this.packageName = null;
}
public boolean supportsUploading() {
@@ -148,6 +154,16 @@
return settingsActivity;
}
+ /**
+ * The package hosting the sync adapter.
+ * @return The package name.
+ *
+ * @hide
+ */
+ public @Nullable String getPackageName() {
+ return packageName;
+ }
+
public static SyncAdapterType newKey(String authority, String accountType) {
return new SyncAdapterType(authority, accountType);
}
@@ -181,6 +197,7 @@
+ ", isAlwaysSyncable=" + isAlwaysSyncable
+ ", allowParallelSyncs=" + allowParallelSyncs
+ ", settingsActivity=" + settingsActivity
+ + ", packageName=" + packageName
+ "}";
}
}
@@ -201,6 +218,7 @@
dest.writeInt(isAlwaysSyncable ? 1 : 0);
dest.writeInt(allowParallelSyncs ? 1 : 0);
dest.writeString(settingsActivity);
+ dest.writeString(packageName);
}
public SyncAdapterType(Parcel source) {
@@ -211,6 +229,7 @@
source.readInt() != 0,
source.readInt() != 0,
source.readInt() != 0,
+ source.readString(),
source.readString());
}
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index 6704b75..ddbdb8a 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -81,7 +81,7 @@
sa.getString(com.android.internal.R.styleable
.SyncAdapter_settingsActivity);
return new SyncAdapterType(authority, accountType, userVisible, supportsUploading,
- isAlwaysSyncable, allowParallelSyncs, settingsActivity);
+ isAlwaysSyncable, allowParallelSyncs, settingsActivity, packageName);
} finally {
sa.recycle();
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index c9be6ed..b5df4d7 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -68,12 +68,6 @@
public EphemeralResolveInfo ephemeralResolveInfo;
/**
- * A ResolveInfo that points at the ephemeral installer.
- * @hide
- */
- public ResolveInfo ephemeralInstaller;
-
- /**
* The IntentFilter that was matched for this ResolveInfo.
*/
public IntentFilter filter;
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 0c3d4b3..d4dcacc 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -123,10 +123,18 @@
/**
* Load a nano app on a specified context hub.
*
+ * Note that loading is asynchronous. When we return from this method,
+ * the nano app (probably) hasn't loaded yet. Assuming a return of 0
+ * from this method, then the final success/failure for the load, along
+ * with the "handle" for the nanoapp, is all delivered in a byte
+ * string via a call to Callback.onMessageReceipt.
+ *
+ * TODO(b/30784270): Provide a better success/failure and "handle" delivery.
+ *
* @param hubHandle handle of context hub to load the app on.
* @param app the nanoApp to load on the hub
*
- * @return int nanoAppInstance of the loaded nanoApp on success,
+ * @return 0 if the command for loading was sent to the context hub;
* -1 otherwise
*
* @see NanoApp
@@ -150,9 +158,17 @@
/**
* Unload a specified nanoApp
*
- * @param nanoAppHandle handle of the nanoApp to load
+ * Note that unloading is asynchronous. When we return from this method,
+ * the nano app (probably) hasn't unloaded yet. Assuming a return of 0
+ * from this method, then the final success/failure for the unload is
+ * delivered in a byte string via a call to Callback.onMessageReceipt.
*
- * @return int 0 on success, -1 otherwise
+ * TODO(b/30784270): Provide a better success/failure delivery.
+ *
+ * @param nanoAppHandle handle of the nanoApp to unload
+ *
+ * @return 0 if the command for unloading was sent to the context hub;
+ * -1 otherwise
*/
public int unloadNanoApp(int nanoAppHandle) {
int retVal = -1;
@@ -169,6 +185,24 @@
/**
* get information about the nano app instance
*
+ * NOTE: The returned NanoAppInstanceInfo does _not_ contain correct
+ * information for several fields, specifically:
+ * - getName()
+ * - getPublisher()
+ * - getNeededExecMemBytes()
+ * - getNeededReadMemBytes()
+ * - getNeededWriteMemBytes()
+ *
+ * For example, say you call loadNanoApp() with a NanoApp that has
+ * getName() returning "My Name". Later, if you call getNanoAppInstanceInfo
+ * for that nanoapp, the returned NanoAppInstanceInfo's getName()
+ * method will claim "Preloaded app, unknown", even though you would
+ * have expected "My Name". For now, as the user, you'll need to
+ * separately track the above fields if they are of interest to you.
+ *
+ * TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the
+ * correct information.
+ *
* @param nanoAppHandle handle of the nanoAppInstance
* @return NanoAppInstanceInfo Information about the nano app instance.
*
@@ -209,6 +243,14 @@
/**
* Send a message to a specific nano app instance on a context hub.
*
+ * Note that the return value of this method only speaks of success
+ * up to the point of sending this to the Context Hub. It is not
+ * an assurance that the Context Hub successfully sent this message
+ * on to the nanoapp. If assurance is desired, a protocol should be
+ * established between your code and the nanoapp, with the nanoapp
+ * sending a confirmation message (which will be reported via
+ * Callback.onMessageReceipt).
+ *
* @param hubHandle handle of the hub to send the message to
* @param nanoAppHandle handle of the nano app to send to
* @param message Message to be sent
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 062c958..eea2387 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -162,6 +162,28 @@
msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
long appId = app.getAppId();
+ // TODO(b/30808791): Remove this hack when the NanoApp API is fixed.
+ // Due to a bug in the NanoApp API, only the least significant four
+ // bytes of the app ID can be stored. The most significant five
+ // bytes of a normal app ID are the "vendor", and thus the most
+ // significant of the bytes we have is the least significant byte
+ // of the vendor. In the case that byte is the ASCII value for
+ // lower-case 'L', we assume the vendor is supposed to be "Googl"
+ // and fill in the four most significant bytes accordingly.
+ if ((appId >> 32) != 0) {
+ // We're unlikely to notice this warning, but at least
+ // we can avoid running our hack logic.
+ Log.w(TAG, "Code has not been updated since API fix.");
+ } else {
+ // Note: Lower-case 'L', not the number 1.
+ if (((appId >> 24) & 0xFF) == (long)'l') {
+ // Assume we're a Google nanoapp.
+ appId |= ((long)'G') << 56;
+ appId |= ((long)'o') << 48;
+ appId |= ((long)'o') << 40;
+ appId |= ((long)'g') << 32;
+ }
+ }
msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);
msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF);
@@ -322,9 +344,16 @@
appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ);
appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ);
+ String action;
+ if (mNanoAppHash.containsKey(appInstanceHandle)) {
+ action = "Updated";
+ } else {
+ action = "Added";
+ }
+
mNanoAppHash.put(appInstanceHandle, appInfo);
- Log.d(TAG, "Added app instance " + appInstanceHandle + " with id " + appId
- + " version " + appVersion);
+ Log.d(TAG, action + " app instance " + appInstanceHandle + " with id "
+ + appId + " version " + appVersion);
return 0;
}
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 8db70e9..bf35a3d 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -43,7 +43,8 @@
private long mAppIdVendorMask;
// Id of the context hub this instance is expected on
- private int mContextHubId;
+ // TODO: Provide an API which will let us change this HubId.
+ private int mContextHubId = HUB_ANY;
/**
* Flag indicating any version. With this flag set, all versions shall match provided version.
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index 71a5a88..ac6d83f 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -113,7 +113,12 @@
}
/**
- * Set the application version
+ * Get the application version
+ *
+ * NOTE: There is a race condition where shortly after loading, this
+ * may return -1 instead of the correct version.
+ *
+ * TODO(b/30970527): Fix this race condition.
*
* @return int - version of the app
*/
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 9cd563e..d570e66 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -175,7 +175,11 @@
if (isNetworkTypeMobile(type)) {
if (state.subscriberId == null) {
- Slog.w(TAG, "Active mobile network without subscriber!");
+ if (state.networkInfo.getState() != NetworkInfo.State.DISCONNECTED &&
+ state.networkInfo.getState() != NetworkInfo.State.UNKNOWN) {
+ Slog.w(TAG, "Active mobile network without subscriber! ni = "
+ + state.networkInfo);
+ }
}
subscriberId = state.subscriberId;
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 4f4e722..9a4b599 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -298,12 +298,16 @@
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
-
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- //noinspection unchecked
- Result result = doInBackground(mParams);
- Binder.flushPendingCommands();
- return postResult(result);
+ Result result = null;
+ try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ //noinspection unchecked
+ result = doInBackground(mParams);
+ Binder.flushPendingCommands();
+ } finally {
+ postResult(result);
+ }
+ return result;
}
};
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index fbd61cf3..c7c6ceb 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -284,8 +284,10 @@
/**
* The cached name associated with the phone number, if it exists.
- * This value is not guaranteed to be current, if the contact information
- * associated with this number has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT</P>
*/
public static final String CACHED_NAME = "name";
@@ -293,8 +295,10 @@
/**
* The cached number type (Home, Work, etc) associated with the
* phone number, if it exists.
- * This value is not guaranteed to be current, if the contact information
- * associated with this number has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: INTEGER</P>
*/
public static final String CACHED_NUMBER_TYPE = "numbertype";
@@ -302,8 +306,10 @@
/**
* The cached number label, for a custom number type, associated with the
* phone number, if it exists.
- * This value is not guaranteed to be current, if the contact information
- * associated with this number has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT</P>
*/
public static final String CACHED_NUMBER_LABEL = "numberlabel";
@@ -339,40 +345,50 @@
/**
* The cached URI to look up the contact associated with the phone number, if it exists.
- * This value may not be current if the contact information associated with this number
- * has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT</P>
*/
public static final String CACHED_LOOKUP_URI = "lookup_uri";
/**
* The cached phone number of the contact which matches this entry, if it exists.
- * This value may not be current if the contact information associated with this number
- * has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT</P>
*/
public static final String CACHED_MATCHED_NUMBER = "matched_number";
/**
* The cached normalized(E164) version of the phone number, if it exists.
- * This value may not be current if the contact information associated with this number
- * has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT</P>
*/
public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
/**
* The cached photo id of the picture associated with the phone number, if it exists.
- * This value may not be current if the contact information associated with this number
- * has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: INTEGER (long)</P>
*/
public static final String CACHED_PHOTO_ID = "photo_id";
/**
* The cached photo URI of the picture associated with the phone number, if it exists.
- * This value may not be current if the contact information associated with this number
- * has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT (URI)</P>
*/
public static final String CACHED_PHOTO_URI = "photo_uri";
@@ -380,9 +396,10 @@
/**
* The cached phone number, formatted with formatting rules based on the country the
* user was in when the call was made or received.
- * This value is not guaranteed to be present, and may not be current if the contact
- * information associated with this number
- * has changed.
+ *
+ * <p>This value is typically filled in by the dialer app for the caching purpose,
+ * so it's not guaranteed to be present, and may not be current if the contact
+ * information associated with this number has changed.
* <P>Type: TEXT</P>
*/
public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index c70304e..c495e6c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -35,6 +35,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.CursorWrapper;
import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
@@ -138,8 +139,20 @@
public static final String DIRECTORY_PARAM_KEY = "directory";
/**
- * A query parameter that limits the number of results returned. The
+ * A query parameter that limits the number of results returned for supported URIs. The
* parameter value should be an integer.
+ *
+ * <p>This parameter is not supported by all URIs. Supported URIs include, but not limited to,
+ * {@link Contacts#CONTENT_URI},
+ * {@link RawContacts#CONTENT_URI},
+ * {@link Data#CONTENT_URI},
+ * {@link CommonDataKinds.Phone#CONTENT_URI},
+ * {@link CommonDataKinds.Callable#CONTENT_URI},
+ * {@link CommonDataKinds.Email#CONTENT_URI},
+ * {@link CommonDataKinds.Contactables#CONTENT_URI},
+ *
+ * <p>In order to limit the number of rows returned by a non-supported URI, you can implement a
+ * {@link CursorWrapper} and override the {@link CursorWrapper#getCount()} methods.
*/
public static final String LIMIT_PARAM_KEY = "limit";
@@ -437,6 +450,9 @@
/**
* _ID of the default directory, which represents locally stored contacts.
+ * <b>This is only supported by {@link ContactsContract.Contacts#CONTENT_URI} and
+ * {@link ContactsContract.Contacts#CONTENT_FILTER_URI}.
+ * Other URLs do not support the concept of "visible" or "invisible" contacts.
*/
public static final long DEFAULT = 0;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 515a91c..79f1151 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5839,6 +5839,8 @@
/**
* If nonzero, ANRs in invisible background processes bring up a dialog.
* Otherwise, the process will be silently killed.
+ *
+ * Also prevents ANRs and crash dialogs from being suppressed.
* @hide
*/
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
@@ -6324,6 +6326,13 @@
= "demo_user_setup_complete";
/**
+ * Specifies whether the web action API is enabled.
+ *
+ * @hide
+ */
+ public static final String WEB_ACTION_ENABLED = "web_action_enabled";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -8743,6 +8752,16 @@
public static final String RETAIL_DEMO_MODE_CONSTANTS = "retail_demo_mode_constants";
/**
+ * The reason for the settings database being downgraded. This is only for
+ * troubleshooting purposes and its value should not be interpreted in any way.
+ *
+ * Type: string
+ *
+ * @hide
+ */
+ public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 286e097..f92d83a 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -96,6 +96,8 @@
private HwuiContext mHwuiContext;
+ private boolean mIsSingleBuffered;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW,
@@ -158,7 +160,7 @@
if (surfaceTexture == null) {
throw new IllegalArgumentException("surfaceTexture must not be null");
}
-
+ mIsSingleBuffered = surfaceTexture.isSingleBuffered();
synchronized (mLock) {
mName = surfaceTexture.toString();
setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
@@ -457,7 +459,10 @@
// create a new native Surface and return it after reducing
// the reference count on mNativeObject. Either way, it is
// not necessary to call nativeRelease() here.
+ // NOTE: This must be kept synchronized with the native parceling code
+ // in frameworks/native/libs/Surface.cpp
mName = source.readString();
+ mIsSingleBuffered = source.readInt() != 0;
setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
}
}
@@ -468,7 +473,10 @@
throw new IllegalArgumentException("dest must not be null");
}
synchronized (mLock) {
+ // NOTE: This must be kept synchronized with the native parceling code
+ // in frameworks/native/libs/Surface.cpp
dest.writeString(mName);
+ dest.writeInt(mIsSingleBuffered ? 1 : 0);
nativeWriteToParcel(mNativeObject, dest);
}
if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
@@ -531,6 +539,14 @@
}
/**
+ * Returns whether or not this Surface is backed by a single-buffered SurfaceTexture
+ * @hide
+ */
+ public boolean isSingleBuffered() {
+ return mIsSingleBuffered;
+ }
+
+ /**
* Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
* when a SurfaceTexture could not successfully be allocated.
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4818910..e0ac7ed 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -193,26 +193,20 @@
private boolean mGlobalListenersAdded;
public SurfaceView(Context context) {
- super(context);
- init();
+ this(context, null);
}
public SurfaceView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
+ this(context, attrs, 0);
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
+ this(context, attrs, defStyleAttr, 0);
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
- private void init() {
setWillNotDraw(true);
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index c7eca44..52b5cf8 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -228,6 +228,7 @@
*/
@Override
protected void destroyHardwareResources() {
+ super.destroyHardwareResources();
destroyHardwareLayer();
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c42ad2e..908658f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20280,8 +20280,14 @@
// remove it from the transparent region.
final int[] location = attachInfo.mTransparentLocation;
getLocationInWindow(location);
- region.op(location[0], location[1], location[0] + mRight - mLeft,
- location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
+ // When a view has Z value, then it will be better to leave some area below the view
+ // for drawing shadow. The shadow outset is proportional to the Z value. Note that
+ // the bottom part needs more offset than the left, top and right parts due to the
+ // spot light effects.
+ int shadowOffset = getZ() > 0 ? (int) getZ() : 0;
+ region.op(location[0] - shadowOffset, location[1] - shadowOffset,
+ location[0] + mRight - mLeft + shadowOffset,
+ location[1] + mBottom - mTop + (shadowOffset * 3), Region.Op.DIFFERENCE);
} else {
if (mBackground != null && mBackground.getOpacity() != PixelFormat.TRANSPARENT) {
// The SKIP_DRAW flag IS set and the background drawable exists, we remove
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3ff8d4f..6933efc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6406,16 +6406,28 @@
return true;
}
super.gatherTransparentRegion(region);
- final View[] children = mChildren;
- final int count = mChildrenCount;
+ // Instead of naively traversing the view tree, we have to traverse according to the Z
+ // order here. We need to go with the same order as dispatchDraw().
+ // One example is that after surfaceView punch a hole, we will still allow other views drawn
+ // on top of that hole. In this case, those other views should be able to cut the
+ // transparent region into smaller area.
+ final int childrenCount = mChildrenCount;
boolean noneOfTheChildrenAreTransparent = true;
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
- if (!child.gatherTransparentRegion(region)) {
- noneOfTheChildrenAreTransparent = false;
+ if (childrenCount > 0) {
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i = 0; i < childrenCount; i++) {
+ final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
+ if (!child.gatherTransparentRegion(region)) {
+ noneOfTheChildrenAreTransparent = false;
+ }
}
}
+ if (preorderedList != null) preorderedList.clear();
}
return meOpaque || noneOfTheChildrenAreTransparent;
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b52e4b0..2b3d643 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -507,6 +507,11 @@
* Retrieves the {@param outBounds} from the stack with id {@param stackId}.
*/
void getStackBounds(int stackId, Rect outBounds);
+
+ /**
+ * Overrides all currently playing app animations with {@param a}.
+ */
+ void overridePlayingAppAnimationsLw(Animation a);
}
public interface PointerEventListener {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 6432f70..5935c78 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1547,7 +1547,7 @@
}
// Let the window manager know to align the top to y.
- outParams.gravity = Gravity.LEFT | Gravity.TOP;
+ outParams.gravity = computeGravity();
outParams.width = width;
outParams.height = height;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index f2fc617..1095978 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -16,19 +16,19 @@
package android.widget;
+import com.android.internal.R;
+
+import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.Widget;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.Parcelable.Creator;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.R;
import java.util.Locale;
@@ -102,8 +102,8 @@
* @param hour the hour to set, in the range (0-23)
* @see #getHour()
*/
- public void setHour(int hour) {
- mDelegate.setHour(hour);
+ public void setHour(@IntRange(from = 0, to = 23) int hour) {
+ mDelegate.setHour(MathUtils.constrain(hour, 0, 23));
}
/**
@@ -117,13 +117,13 @@
}
/**
- * Sets the currently selected minute..
+ * Sets the currently selected minute.
*
* @param minute the minute to set, in the range (0-59)
* @see #getMinute()
*/
- public void setMinute(int minute) {
- mDelegate.setMinute(minute);
+ public void setMinute(@IntRange(from = 0, to = 59) int minute) {
+ mDelegate.setMinute(MathUtils.constrain(minute, 0, 59));
}
/**
@@ -137,8 +137,9 @@
}
/**
- * Sets the current hour.
+ * Sets the currently selected hour using 24-hour time.
*
+ * @param currentHour the hour to set, in the range (0-23)
* @deprecated Use {@link #setHour(int)}
*/
@Deprecated
@@ -147,33 +148,34 @@
}
/**
- * @return the current hour in the range (0-23)
+ * @return the currently selected hour, in the range (0-23)
* @deprecated Use {@link #getHour()}
*/
@NonNull
@Deprecated
public Integer getCurrentHour() {
- return mDelegate.getHour();
+ return getHour();
}
/**
- * Set the current minute (0-59).
+ * Sets the currently selected minute.
*
+ * @param currentMinute the minute to set, in the range (0-59)
* @deprecated Use {@link #setMinute(int)}
*/
@Deprecated
public void setCurrentMinute(@NonNull Integer currentMinute) {
- mDelegate.setMinute(currentMinute);
+ setMinute(currentMinute);
}
/**
- * @return the current minute
+ * @return the currently selected minute, in the range (0-59)
* @deprecated Use {@link #getMinute()}
*/
@NonNull
@Deprecated
public Integer getCurrentMinute() {
- return mDelegate.getMinute();
+ return getMinute();
}
/**
@@ -256,10 +258,10 @@
* for the real behavior.
*/
interface TimePickerDelegate {
- void setHour(int hour);
+ void setHour(@IntRange(from = 0, to = 23) int hour);
int getHour();
- void setMinute(int minute);
+ void setMinute(@IntRange(from = 0, to = 59) int minute);
int getMinute();
void setIs24Hour(boolean is24Hour);
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index f084db2..b973324 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -70,46 +70,49 @@
*/
public class VideoView extends SurfaceView
implements MediaPlayerControl, SubtitleController.Anchor {
- private String TAG = "VideoView";
- // settable by the client
- private Uri mUri;
- private Map<String, String> mHeaders;
+ private static final String TAG = "VideoView";
// all possible internal states
- private static final int STATE_ERROR = -1;
- private static final int STATE_IDLE = 0;
- private static final int STATE_PREPARING = 1;
- private static final int STATE_PREPARED = 2;
- private static final int STATE_PLAYING = 3;
- private static final int STATE_PAUSED = 4;
+ private static final int STATE_ERROR = -1;
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_PREPARING = 1;
+ private static final int STATE_PREPARED = 2;
+ private static final int STATE_PLAYING = 3;
+ private static final int STATE_PAUSED = 4;
private static final int STATE_PLAYBACK_COMPLETED = 5;
+ private final Vector<Pair<InputStream, MediaFormat>> mPendingSubtitleTracks = new Vector<>();
+
+ // settable by the client
+ private Uri mUri;
+ private Map<String, String> mHeaders;
+
// mCurrentState is a VideoView object's current state.
// mTargetState is the state that a method caller intends to reach.
// For instance, regardless the VideoView object's current state,
// calling pause() intends to bring the object to a target state
// of STATE_PAUSED.
private int mCurrentState = STATE_IDLE;
- private int mTargetState = STATE_IDLE;
+ private int mTargetState = STATE_IDLE;
// All the stuff we need for playing and showing a video
private SurfaceHolder mSurfaceHolder = null;
private MediaPlayer mMediaPlayer = null;
- private int mAudioSession;
- private int mVideoWidth;
- private int mVideoHeight;
- private int mSurfaceWidth;
- private int mSurfaceHeight;
+ private int mAudioSession;
+ private int mVideoWidth;
+ private int mVideoHeight;
+ private int mSurfaceWidth;
+ private int mSurfaceHeight;
private MediaController mMediaController;
private OnCompletionListener mOnCompletionListener;
private MediaPlayer.OnPreparedListener mOnPreparedListener;
- private int mCurrentBufferPercentage;
+ private int mCurrentBufferPercentage;
private OnErrorListener mOnErrorListener;
- private OnInfoListener mOnInfoListener;
- private int mSeekWhenPrepared; // recording the seek position while preparing
- private boolean mCanPause;
- private boolean mCanSeekBack;
- private boolean mCanSeekForward;
+ private OnInfoListener mOnInfoListener;
+ private int mSeekWhenPrepared; // recording the seek position while preparing
+ private boolean mCanPause;
+ private boolean mCanSeekBack;
+ private boolean mCanSeekForward;
/** Subtitle rendering widget overlaid on top of the video. */
private RenderingWidget mSubtitleWidget;
@@ -118,13 +121,11 @@
private RenderingWidget.OnChangedListener mSubtitlesChangedListener;
public VideoView(Context context) {
- super(context);
- initVideoView();
+ this(context, null);
}
public VideoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
- initVideoView();
}
public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -133,7 +134,19 @@
public VideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- initVideoView();
+
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+
+ getHolder().addCallback(mSHCallback);
+ getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
}
@Override
@@ -209,19 +222,6 @@
return getDefaultSize(desiredSize, measureSpec);
}
- private void initVideoView() {
- mVideoWidth = 0;
- mVideoHeight = 0;
- getHolder().addCallback(mSHCallback);
- getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
- mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();
- mCurrentState = STATE_IDLE;
- mTargetState = STATE_IDLE;
- }
-
/**
* Sets video path.
*
@@ -294,8 +294,6 @@
}
}
- private Vector<Pair<InputStream, MediaFormat>> mPendingSubtitleTracks;
-
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
diff --git a/core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java b/core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java
new file mode 100644
index 0000000..62f18ea
--- /dev/null
+++ b/core/java/com/android/internal/graphics/drawable/AnimationScaleListDrawable.java
@@ -0,0 +1,254 @@
+/*
+ * 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 com.android.internal.graphics.drawable;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * An internal DrawableContainer class, used to draw different things depending on animation scale.
+ * i.e: animation scale can be 0 in battery saver mode.
+ * This class contains 2 drawable, one is animatable, the other is static. When animation scale is
+ * not 0, the animatable drawable will the drawn. Otherwise, the static drawable will be drawn.
+ * <p>This class implements Animatable since ProgressBar can pick this up similarly as an
+ * AnimatedVectorDrawable.
+ * <p>It can be defined in an XML file with the {@code <AnimationScaleListDrawable>}
+ * element.
+ */
+public class AnimationScaleListDrawable extends DrawableContainer implements Animatable {
+ private static final String TAG = "AnimationScaleListDrawable";
+ private AnimationScaleListState mAnimationScaleListState;
+ private boolean mMutated;
+
+ public AnimationScaleListDrawable() {
+ this(null, null);
+ }
+
+ private AnimationScaleListDrawable(@Nullable AnimationScaleListState state,
+ @Nullable Resources res) {
+ // Every scale list drawable has its own constant state.
+ final AnimationScaleListState newState = new AnimationScaleListState(state, this, res);
+ setConstantState(newState);
+ onStateChange(getState());
+ }
+
+ /**
+ * Set the current drawable according to the animation scale. If scale is 0, then pick the
+ * static drawable, otherwise, pick the animatable drawable.
+ */
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final boolean changed = super.onStateChange(stateSet);
+ int idx = mAnimationScaleListState.getCurrentDrawableIndexBasedOnScale();
+ return selectDrawable(idx) || changed;
+ }
+
+
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs,
+ R.styleable.AnimationScaleListDrawable);
+ updateDensity(r);
+ a.recycle();
+
+ inflateChildElements(r, parser, attrs, theme);
+
+ onStateChange(getState());
+ }
+
+ /**
+ * Inflates child elements from XML.
+ */
+ private void inflateChildElements(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ final AnimationScaleListState state = mAnimationScaleListState;
+ final int innerDepth = parser.getDepth() + 1;
+ int type;
+ int depth;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && ((depth = parser.getDepth()) >= innerDepth
+ || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth > innerDepth || !parser.getName().equals("item")) {
+ continue;
+ }
+
+ // Either pick up the android:drawable attribute.
+ final TypedArray a = obtainAttributes(r, theme, attrs,
+ R.styleable.AnimationScaleListDrawableItem);
+ Drawable dr = a.getDrawable(R.styleable.AnimationScaleListDrawableItem_drawable);
+ a.recycle();
+
+ // Or parse the child element under <item>.
+ if (dr == null) {
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription()
+ + ": <item> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ }
+
+ state.addDrawable(dr);
+ }
+ }
+
+ @Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mAnimationScaleListState.mutate();
+ mMutated = true;
+ }
+ return this;
+ }
+
+ @Override
+ public void clearMutated() {
+ super.clearMutated();
+ mMutated = false;
+ }
+
+ @Override
+ public void start() {
+ Drawable dr = getCurrent();
+ if (dr != null && dr instanceof Animatable) {
+ ((Animatable) dr).start();
+ }
+ }
+
+ @Override
+ public void stop() {
+ Drawable dr = getCurrent();
+ if (dr != null && dr instanceof Animatable) {
+ ((Animatable) dr).stop();
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ boolean result = false;
+ Drawable dr = getCurrent();
+ if (dr != null && dr instanceof Animatable) {
+ result = ((Animatable) dr).isRunning();
+ }
+ return result;
+ }
+
+ static class AnimationScaleListState extends DrawableContainerState {
+ int[] mThemeAttrs = null;
+ // The index of the last static drawable.
+ int mStaticDrawableIndex = -1;
+ // The index of the last animatable drawable.
+ int mAnimatableDrawableIndex = -1;
+
+ AnimationScaleListState(AnimationScaleListState orig, AnimationScaleListDrawable owner,
+ Resources res) {
+ super(orig, owner, res);
+
+ if (orig != null) {
+ // Perform a shallow copy and rely on mutate() to deep-copy.
+ mThemeAttrs = orig.mThemeAttrs;
+
+ mStaticDrawableIndex = orig.mStaticDrawableIndex;
+ mAnimatableDrawableIndex = orig.mAnimatableDrawableIndex;
+ }
+
+ }
+
+ void mutate() {
+ mThemeAttrs = mThemeAttrs != null ? mThemeAttrs.clone() : null;
+ }
+
+ /**
+ * Add the drawable into the container.
+ * This class only keep track one animatable drawable, and one static. If there are multiple
+ * defined in the XML, then pick the last one.
+ */
+ int addDrawable(Drawable drawable) {
+ final int pos = addChild(drawable);
+ if (drawable instanceof Animatable) {
+ mAnimatableDrawableIndex = pos;
+ } else {
+ mStaticDrawableIndex = pos;
+ }
+ return pos;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new AnimationScaleListDrawable(this, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res) {
+ return new AnimationScaleListDrawable(this, res);
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null || super.canApplyTheme();
+ }
+
+ public int getCurrentDrawableIndexBasedOnScale() {
+ if (ValueAnimator.getDurationScale() == 0) {
+ return mStaticDrawableIndex;
+ }
+ return mAnimatableDrawableIndex;
+ }
+ }
+
+ @Override
+ public void applyTheme(@NonNull Theme theme) {
+ super.applyTheme(theme);
+
+ onStateChange(getState());
+ }
+
+ @Override
+ protected void setConstantState(@NonNull DrawableContainerState state) {
+ super.setConstantState(state);
+
+ if (state instanceof AnimationScaleListState) {
+ mAnimationScaleListState = (AnimationScaleListState) state;
+ }
+ }
+}
+
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index b7e5718..e46dfc4 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -39,6 +39,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
+import java.util.List;
import java.util.StringTokenizer;
public class ProcessCpuTracker {
@@ -177,6 +178,11 @@
private byte[] mBuffer = new byte[4096];
+ public interface FilterStats {
+ /** Which stats to pick when filtering */
+ boolean needed(Stats stats);
+ }
+
public static class Stats {
public final int pid;
public final int uid;
@@ -695,6 +701,18 @@
return mProcStats.get(index);
}
+ final public List<Stats> getStats(FilterStats filter) {
+ final ArrayList<Stats> statses = new ArrayList<>(mProcStats.size());
+ final int N = mProcStats.size();
+ for (int p = 0; p < N; p++) {
+ Stats stats = mProcStats.get(p);
+ if (filter.needed(stats)) {
+ statses.add(stats);
+ }
+ }
+ return statses;
+ }
+
final public int countWorkingStats() {
buildWorkingProcs();
return mWorkingProcs.size();
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 83d75fb..e51ad3f 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -28,8 +28,9 @@
* FLAG_SHOW_ON_LOCK_SCREEN.
*
* @param isOccluded Whether the Keyguard is occluded by another window.
+ * @param animate Whether to play an animation for the state change.
*/
- void setOccluded(boolean isOccluded);
+ void setOccluded(boolean isOccluded, boolean animate);
void addStateMonitorCallback(IKeyguardStateCallback callback);
void verifyUnlock(IKeyguardExitCallback callback);
diff --git a/core/java/com/android/internal/widget/AlertDialogLayout.java b/core/java/com/android/internal/widget/AlertDialogLayout.java
index 891c920..9bf0948 100644
--- a/core/java/com/android/internal/widget/AlertDialogLayout.java
+++ b/core/java/com/android/internal/widget/AlertDialogLayout.java
@@ -20,7 +20,9 @@
import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
@@ -265,4 +267,92 @@
return 0;
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int paddingLeft = mPaddingLeft;
+
+ // Where right end of child should go
+ final int width = right - left;
+ final int childRight = width - mPaddingRight;
+
+ // Space available for child
+ final int childSpace = width - paddingLeft - mPaddingRight;
+
+ final int totalLength = getMeasuredHeight();
+ final int count = getChildCount();
+ final int gravity = getGravity();
+ final int majorGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ final int minorGravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+
+ int childTop;
+ switch (majorGravity) {
+ case Gravity.BOTTOM:
+ // totalLength contains the padding already
+ childTop = mPaddingTop + bottom - top - totalLength;
+ break;
+
+ // totalLength contains the padding already
+ case Gravity.CENTER_VERTICAL:
+ childTop = mPaddingTop + (bottom - top - totalLength) / 2;
+ break;
+
+ case Gravity.TOP:
+ default:
+ childTop = mPaddingTop;
+ break;
+ }
+
+ final Drawable dividerDrawable = getDividerDrawable();
+ final int dividerHeight = dividerDrawable == null ?
+ 0 : dividerDrawable.getIntrinsicHeight();
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child != null && child.getVisibility() != GONE) {
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ final LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) child.getLayoutParams();
+
+ int layoutGravity = lp.gravity;
+ if (layoutGravity < 0) {
+ layoutGravity = minorGravity;
+ }
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(
+ layoutGravity, layoutDirection);
+
+ final int childLeft;
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ + lp.leftMargin - lp.rightMargin;
+ break;
+
+ case Gravity.RIGHT:
+ childLeft = childRight - childWidth - lp.rightMargin;
+ break;
+
+ case Gravity.LEFT:
+ default:
+ childLeft = paddingLeft + lp.leftMargin;
+ break;
+ }
+
+ if (hasDividerBeforeChildAt(i)) {
+ childTop += dividerHeight;
+ }
+
+ childTop += lp.topMargin;
+ setChildFrame(child, childLeft, childTop, childWidth, childHeight);
+ childTop += childHeight + lp.bottomMargin;
+ }
+ }
+ }
+
+ private void setChildFrame(View child, int left, int top, int width, int height) {
+ child.layout(left, top, left + width, top + height);
+ }
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index fbc51cd..5a50fbf 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -79,6 +79,9 @@
private static final String LOG_FILES_FILE = "log-files.xml";
private static final AtomicFile sFile = new AtomicFile(new File(
Environment.getDataSystemDirectory(), LOG_FILES_FILE));
+ private static final String LAST_HEADER_FILE = "last-header.txt";
+ private static final File lastHeaderFile = new File(
+ Environment.getDataSystemDirectory(), LAST_HEADER_FILE);
@Override
public void onReceive(final Context context, Intent intent) {
@@ -113,9 +116,17 @@
Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
}
- private void logBootEvents(Context ctx) throws IOException {
- final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);
- final String headers = new StringBuilder(512)
+ private String getPreviousBootHeaders() {
+ try {
+ return FileUtils.readTextFile(lastHeaderFile, 0, null);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + lastHeaderFile, e);
+ return null;
+ }
+ }
+
+ private String getCurrentBootHeaders() throws IOException {
+ return new StringBuilder(512)
.append("Build: ").append(Build.FINGERPRINT).append("\n")
.append("Hardware: ").append(Build.BOARD).append("\n")
.append("Revision: ")
@@ -125,6 +136,31 @@
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
+ }
+
+
+ private String getBootHeadersToLogAndUpdate() throws IOException {
+ final String oldHeaders = getPreviousBootHeaders();
+ final String newHeaders = getCurrentBootHeaders();
+
+ try {
+ FileUtils.stringToFile(lastHeaderFile, newHeaders);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error writing " + lastHeaderFile, e);
+ }
+
+ if (oldHeaders == null) {
+ // If we failed to read the old headers, use the current headers
+ // but note this in the headers so we know
+ return "isPrevious: false\n" + newHeaders;
+ }
+
+ return "isPrevious: true\n" + oldHeaders;
+ }
+
+ private void logBootEvents(Context ctx) throws IOException {
+ final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);
+ final String headers = getBootHeadersToLogAndUpdate();
final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
String recovery = RecoverySystem.handleAftermath(ctx);
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index d2d39cd..0468b64 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -52,11 +52,9 @@
} gRectClassInfo;
// Also used in PdfRenderer.cpp
-Mutex sPdfiumLock;
int sUnmatchedPdfiumInitRequestCount = 0;
static void initializeLibraryIfNeeded() {
- Mutex::Autolock _l(sPdfiumLock);
if (sUnmatchedPdfiumInitRequestCount == 0) {
FPDF_InitLibrary();
}
@@ -64,7 +62,6 @@
}
static void destroyLibraryIfNeeded() {
- Mutex::Autolock _l(sPdfiumLock);
sUnmatchedPdfiumInitRequestCount--;
if (sUnmatchedPdfiumInitRequestCount == 0) {
FPDF_DestroyLibrary();
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index 71bec78..43550ac 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -44,11 +44,9 @@
} gPointClassInfo;
// See PdfEditor.cpp
-extern Mutex sPdfiumLock;
extern int sUnmatchedPdfiumInitRequestCount;
static void initializeLibraryIfNeeded() {
- Mutex::Autolock _l(sPdfiumLock);
if (sUnmatchedPdfiumInitRequestCount == 0) {
FPDF_InitLibrary();
}
@@ -56,7 +54,6 @@
}
static void destroyLibraryIfNeeded() {
- Mutex::Autolock _l(sPdfiumLock);
sUnmatchedPdfiumInitRequestCount--;
if (sUnmatchedPdfiumInitRequestCount == 0) {
FPDF_DestroyLibrary();
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index bb09d00..e96613b 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -16,6 +16,7 @@
#undef LOG_TAG
#define LOG_TAG "CursorWindow"
+#define LOG_NDEBUG 0
#include <inttypes.h>
#include <jni.h>
@@ -30,6 +31,11 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#undef LOG_NDEBUG
+#define LOG_NDEBUG 1
#include <androidfw/CursorWindow.h>
#include "android_os_Parcel.h"
@@ -61,6 +67,22 @@
jniThrowException(env, "java/lang/IllegalStateException", msg.string());
}
+static int getFdCount() {
+ char fdpath[PATH_MAX];
+ int count = 0;
+ snprintf(fdpath, PATH_MAX, "/proc/%d/fd", getpid());
+ DIR *dir = opendir(fdpath);
+ if (dir != NULL) {
+ struct dirent *dirent;
+ while ((dirent = readdir(dir))) {
+ count++;
+ }
+ count -= 2; // discount "." and ".."
+ closedir(dir);
+ }
+ return count;
+}
+
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
String8 name;
const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
@@ -85,7 +107,8 @@
CursorWindow* window;
status_t status = CursorWindow::createFromParcel(parcel, &window);
if (status || !window) {
- ALOGE("Could not create CursorWindow from Parcel due to error %d.", status);
+ ALOGE("Could not create CursorWindow from Parcel due to error %d, process fd count=%d",
+ status, getFdCount());
return 0;
}
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 9515a0e..3644410 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -26,6 +26,10 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+
+// TOOD: On master, alphabetize these and move <mutex> into this
+// grouping.
+#include <chrono>
#include <unordered_map>
#include <queue>
@@ -34,11 +38,12 @@
#include "JNIHelp.h"
#include "core_jni_helpers.h"
-static constexpr int OS_APP_ID = -1;
+static constexpr jint OS_APP_ID = -1;
+static constexpr jint INVALID_APP_ID = -2;
static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF);
-static constexpr int MIN_APP_ID = 1;
-static constexpr int MAX_APP_ID = 128;
+static constexpr jint MIN_APP_ID = 1;
+static constexpr jint MAX_APP_ID = 128;
static constexpr size_t MSG_HEADER_SIZE = 4;
static constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
@@ -50,6 +55,10 @@
static constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
static constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
+// Monotonically increasing clock we use to determine if we can cancel
+// a transaction.
+using std::chrono::steady_clock;
+
namespace android {
namespace {
@@ -102,10 +111,21 @@
struct app_instance_info_s {
uint64_t truncName; // Possibly truncated name for logging
uint32_t hubHandle; // Id of the hub this app is on
- int instanceId; // system wide unique instance id - assigned
+ jint instanceId; // system wide unique instance id - assigned
struct hub_app_info appInfo; // returned from the HAL
};
+
+// If a transaction takes longer than this, we'll allow it to be
+// canceled by a new transaction. Note we do _not_ automatically
+// cancel a transaction after this much time. We can have a
+// legal transaction which takes longer than this amount of time,
+// as long as no other new transactions are attempted after this
+// time has expired.
+// TODO(b/31105001): Establish a clean timing approach for all
+// of our HAL interactions.
+constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29);
+
/*
* TODO(ashutoshj): From original code review:
*
@@ -147,14 +167,15 @@
std::mutex m; // mutex for manager
hub_messages_e txnIdentifier; // What are we doing
void *txnData; // Details
+ steady_clock::time_point firstTimeTxnCanBeCanceled;
};
struct contextHubServiceDb_s {
int initialized;
context_hub_info_s hubInfo;
jniInfo_s jniInfo;
- std::queue<int> freeIds;
- std::unordered_map<int, app_instance_info_s> appInstances;
+ std::queue<jint> freeIds;
+ std::unordered_map<jint, app_instance_info_s> appInstances;
txnManager_s txnManager;
};
@@ -176,25 +197,40 @@
std::lock_guard<std::mutex>lock(mgr->m);
mgr->txnPending = true;
+ mgr->firstTimeTxnCanBeCanceled = steady_clock::now() +
+ kMinTransactionCancelTime;
mgr->txnData = txnData;
mgr->txnIdentifier = txnIdentifier;
return 0;
}
-static int closeTxn() {
+// Only call this if you hold the db.txnManager.m lock.
+static void closeTxnUnlocked() {
txnManager_s *mgr = &db.txnManager;
- std::lock_guard<std::mutex>lock(mgr->m);
mgr->txnPending = false;
free(mgr->txnData);
mgr->txnData = nullptr;
+}
+static int closeTxn() {
+ std::lock_guard<std::mutex>lock(db.txnManager.m);
+ closeTxnUnlocked();
return 0;
}
+// If a transaction has been pending for longer than
+// kMinTransactionCancelTime, this call will "cancel" that
+// transaction and return that there are none pending.
static bool isTxnPending() {
txnManager_s *mgr = &db.txnManager;
std::lock_guard<std::mutex>lock(mgr->m);
+ if (mgr->txnPending) {
+ if (steady_clock::now() >= mgr->firstTimeTxnCanBeCanceled) {
+ ALOGW("Transaction canceled");
+ closeTxnUnlocked();
+ }
+ }
return mgr->txnPending;
}
@@ -259,16 +295,17 @@
}
}
-static int get_hub_handle_for_app_instance(int id) {
+static int get_hub_handle_for_app_instance(jint id) {
if (!db.appInstances.count(id)) {
- ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
+ ALOGD("%s: Cannot find app for app instance %" PRId32,
+ __FUNCTION__, id);
return -1;
}
return db.appInstances[id].hubHandle;
}
-static int get_hub_id_for_app_instance(int id) {
+static int get_hub_id_for_app_instance(jint id) {
int hubHandle = get_hub_handle_for_app_instance(id);
if (hubHandle < 0) {
@@ -278,7 +315,7 @@
return db.hubInfo.hubs[hubHandle].hub_id;
}
-static int get_app_instance_for_app_id(uint64_t app_id) {
+static jint get_app_instance_for_app_id(uint64_t app_id) {
auto end = db.appInstances.end();
for (auto current = db.appInstances.begin(); current != end; ++current) {
if (current->second.appInfo.app_name.id == app_id) {
@@ -289,9 +326,10 @@
return -1;
}
-static int set_dest_app(hub_message_t *msg, int id) {
+static int set_dest_app(hub_message_t *msg, jint id) {
if (!db.appInstances.count(id)) {
- ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
+ ALOGD("%s: Cannot find app for app instance %" PRId32,
+ __FUNCTION__, id);
return -1;
}
@@ -303,7 +341,7 @@
hub_message_t msg;
query_apps_request_t queryMsg;
- queryMsg.app_name.id = NANOAPP_VENDOR_ALL_APPS;
+ queryMsg.app_name.id = appId;
msg.message_type = CONTEXT_HUB_QUERY_APPS;
msg.message_len = sizeof(queryMsg);
@@ -322,7 +360,7 @@
}
}
-static int return_id(int id) {
+static int return_id(jint id) {
// Note : This method is not thread safe.
// id returned is guaranteed to be in use
if (id >= 0) {
@@ -333,9 +371,9 @@
return -1;
}
-static int generate_id() {
+static jint generate_id() {
// Note : This method is not thread safe.
- int retVal = -1;
+ jint retVal = -1;
if (!db.freeIds.empty()) {
retVal = db.freeIds.front();
@@ -346,8 +384,8 @@
}
-static int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle,
- int appInstanceHandle, JNIEnv *env) {
+static jint add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle,
+ jint appInstanceHandle, JNIEnv *env) {
ALOGI("Loading App");
@@ -355,10 +393,12 @@
app_instance_info_s entry;
assert(appInfo);
+ const char *action = "Updated";
if (db.appInstances.count(appInstanceHandle) == 0) {
+ action = "Added";
appInstanceHandle = generate_id();
if (appInstanceHandle < 0) {
- ALOGE("Cannot find resources to add app instance %d",
+ ALOGE("Cannot find resources to add app instance %" PRId32,
appInstanceHandle);
return -1;
}
@@ -378,36 +418,42 @@
hubHandle, entry.instanceId, entry.truncName,
entry.appInfo.version);
- ALOGW("Added App 0x%" PRIx64 " on hub Handle %" PRId32
- " as appInstance %d", entry.truncName,
+ ALOGW("%s App 0x%" PRIx64 " on hub Handle %" PRId32
+ " as appInstance %" PRId32, action, entry.truncName,
entry.hubHandle, appInstanceHandle);
return appInstanceHandle;
}
-int delete_app_instance(int id, JNIEnv *env) {
- if (!db.appInstances.count(id)) {
- ALOGW("Cannot find App id : %d", id);
- return -1;
- }
+int delete_app_instance(jint id, JNIEnv *env) {
+ bool fullyDeleted = true;
+ if (db.appInstances.count(id)) {
+ db.appInstances.erase(id);
+ } else {
+ ALOGW("Cannot delete App id (%" PRId32 ") from the JNI C++ cache", id);
+ fullyDeleted = false;
+ }
return_id(id);
- db.appInstances.erase(id);
- if (env->CallIntMethod(db.jniInfo.jContextHubService,
+
+ if ((env == nullptr) ||
+ (env->CallIntMethod(db.jniInfo.jContextHubService,
db.jniInfo.contextHubServiceDeleteAppInstance,
- id) != 0) {
- ALOGW("Could not delete App id : %d", id);
- return -1;
+ id) != 0)) {
+ ALOGW("Cannot delete App id (%" PRId32 ") from Java cache", id);
+ fullyDeleted = false;
}
- ALOGI("Deleted App id : %d", id);
-
- return 0;
+ if (fullyDeleted) {
+ ALOGI("Deleted App id : %" PRId32, id);
+ return 0;
+ }
+ return -1;
}
static int startLoadAppTxn(uint64_t appId, int hubHandle) {
app_instance_info_s *txnInfo = (app_instance_info_s *)malloc(sizeof(app_instance_info_s));
- int instanceId = generate_id();
+ jint instanceId = generate_id();
if (!txnInfo || instanceId < 0) {
return_id(instanceId);
@@ -432,8 +478,8 @@
return 0;
}
-static int startUnloadAppTxn(uint32_t appInstanceHandle) {
- uint32_t *txnData = (uint32_t *) malloc(sizeof(uint32_t));
+static int startUnloadAppTxn(jint appInstanceHandle) {
+ jint *txnData = (jint *) malloc(sizeof(jint));
if (!txnData) {
ALOGW("Cannot allocate memory to start unload transaction");
return -1;
@@ -454,7 +500,6 @@
int err = 0;
db.hubInfo.hubs = nullptr;
db.hubInfo.numHubs = 0;
- int i;
err = hw_get_module(CONTEXT_HUB_MODULE_ID,
(hw_module_t const**)(&db.hubInfo.contextHubModule));
@@ -465,7 +510,7 @@
}
// Prep for storing app info
- for(i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
+ for (jint i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
db.freeIds.push(i);
}
@@ -485,7 +530,7 @@
return;
}
- for (i = 0; i < db.hubInfo.numHubs; i++) {
+ for (int i = 0; i < db.hubInfo.numHubs; i++) {
db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id;
ALOGI("Subscribing to hubHandle %d with OS App name %" PRIu64, i, db.hubInfo.hubs[i].os_app_name.id);
if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
@@ -547,13 +592,15 @@
memcpy(&info, unalignedInfoAddr, sizeof(info));
// We will only have one instance of the app
// TODO : Change this logic once we support multiple instances of the same app
- int appInstance = get_app_instance_for_app_id(info.app_name.id);
+ jint appInstance = get_app_instance_for_app_id(info.app_name.id);
add_app_instance(&info, hubHandle, appInstance, env);
}
return 0;
}
+// TODO(b/30807327): Do not use raw bytes for additional data. Use the
+// JNI interfaces for the appropriate types.
static void passOnOsResponse(uint32_t hubHandle, uint32_t msgType,
status_response_t *rsp, int8_t *additionalData,
size_t additionalDataLen) {
@@ -584,7 +631,32 @@
header[HEADER_FIELD_HUB_HANDLE] = hubHandle;
header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID;
- msg[0] = rsp->result;
+ // Due to API constraints, at the moment we can't change the fact that
+ // we're changing our 4-byte response to a 1-byte value. But we can prevent
+ // the possible change in sign (and thus meaning) that would happen from
+ // a naive cast. Further, we can log when we're losing part of the value.
+ // TODO(b/30918279): Don't truncate this result.
+ int8_t truncatedResult;
+ bool neededToTruncate;
+ if (rsp->result < INT8_MIN) {
+ neededToTruncate = true;
+ truncatedResult = INT8_MIN;
+ } else if (rsp->result > INT8_MAX) {
+ neededToTruncate = true;
+ truncatedResult = INT8_MAX;
+ } else {
+ neededToTruncate = false;
+ // Since this value fits within an int8_t, this is a safe cast which
+ // won't change the value or sign.
+ truncatedResult = static_cast<int8_t>(rsp->result);
+ }
+ if (neededToTruncate) {
+ ALOGW("Response from Context Hub truncated. Value was %" PRId32
+ ", but giving Java layer %" PRId8,
+ rsp->result, (int)truncatedResult);
+ }
+
+ msg[0] = truncatedResult;
if (additionalData) {
memcpy(&msg[1], additionalData, additionalDataLen);
@@ -603,6 +675,8 @@
env->CallIntMethod(db.jniInfo.jContextHubService,
db.jniInfo.contextHubServiceMsgReceiptCallback,
jheader, jmsg);
+ env->DeleteLocalRef(jmsg);
+ env->DeleteLocalRef(jheader);
delete[] msg;
}
@@ -613,7 +687,13 @@
if (success && fetchTxnData(&txnId, &txnData) == 0 &&
txnId == CONTEXT_HUB_UNLOAD_APP) {
- db.appInstances.erase(*(uint32_t *)txnData);
+ JNIEnv *env;
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
+ ALOGW("Could not attach to JVM !");
+ env = nullptr;
+ }
+ jint handle = *reinterpret_cast<jint *>(txnData);
+ delete_app_instance(handle, env);
} else {
ALOGW("Could not unload the app successfully ! success %d, txnData %p", success, txnData);
}
@@ -621,7 +701,7 @@
closeTxn();
}
-void closeLoadTxn(bool success, int *appInstanceHandle) {
+static bool closeLoadTxn(bool success, jint *appInstanceHandle) {
void *txnData;
hub_messages_e txnId;
@@ -635,13 +715,17 @@
add_app_instance(&info->appInfo, info->hubHandle, info->instanceId, env);
} else {
ALOGW("Could not attach to JVM !");
+ success = false;
}
sendQueryForApps(info->appInfo.app_name.id);
} else {
ALOGW("Could not load the app successfully ! Unexpected failure");
+ *appInstanceHandle = INVALID_APP_ID;
+ success = false;
}
closeTxn();
+ return success;
}
static bool isValidOsStatus(const uint8_t *msg, size_t msgLen,
@@ -668,6 +752,7 @@
if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
ALOGW("Could not attach to JVM !");
+ env = nullptr;
}
auto end = db.appInstances.end();
@@ -697,8 +782,27 @@
case CONTEXT_HUB_UNLOAD_APP:
if (isValidOsStatus(msg, msgLen, &rsp)) {
if (msgType == CONTEXT_HUB_LOAD_APP) {
- int appInstanceHandle;
- closeLoadTxn(rsp.result == 0, &appInstanceHandle);
+ jint appInstanceHandle = INVALID_APP_ID;
+ bool appRunningOnHub = (rsp.result == 0);
+ if (!(closeLoadTxn(appRunningOnHub, &appInstanceHandle))) {
+ if (appRunningOnHub) {
+ // Now we're in an odd situation. Our nanoapp
+ // is up and running on the Context Hub. However,
+ // something went wrong in our Service code so that
+ // we're not able to properly track this nanoapp
+ // in our Service code. If we tell the Java layer
+ // things are good, it's a lie because the handle
+ // we give them will fail when used with the Service.
+ // If we tell the Java layer this failed, it's kind
+ // of a lie as well, since this nanoapp is running.
+ //
+ // We leave a more robust fix for later, and for
+ // now just tell the user things have failed.
+ //
+ // TODO(b/30835981): Make this situation better.
+ rsp.result = -1;
+ }
+ }
passOnOsResponse(hubHandle, msgType, &rsp, (int8_t *)(&appInstanceHandle),
sizeof(appInstanceHandle));
} else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
@@ -778,7 +882,7 @@
if (messageType < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) {
handle_os_message(messageType, hubHandle, (uint8_t*) msg->message, msg->message_len);
} else {
- int appHandle = get_app_instance_for_app_id(msg->app_name.id);
+ jint appHandle = get_app_instance_for_app_id(msg->app_name.id);
if (appHandle < 0) {
ALOGE("Filtering out message due to invalid App Instance.");
} else {
@@ -1051,7 +1155,8 @@
ALOGD("Asking HAL to remove app");
retVal = db.hubInfo.contextHubModule->send_message(hubId, &msg);
} else {
- ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d",
+ ALOGD("Could not find app instance %" PRId32 " on hubHandle %" PRId32
+ ", setAddress %d",
header[HEADER_FIELD_APP_INSTANCE],
header[HEADER_FIELD_HUB_HANDLE],
(int)setAddressSuccess);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0d8a95c..73b3f52 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -371,7 +371,12 @@
if (sur != NULL) {
bufferProducer = sur->getIGraphicBufferProducer();
}
- SurfaceComposerClient::setDisplaySurface(token, bufferProducer);
+ status_t err = SurfaceComposerClient::setDisplaySurface(token,
+ bufferProducer);
+ if (err != NO_ERROR) {
+ doThrowIAE(env, "Illegal Surface, could not enable async mode. Was this"
+ " Surface created with singleBufferMode?");
+ }
}
static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index a04fc2a..d1fe07b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -54,6 +54,7 @@
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
+#include "fd_utils-inl.h"
#include "nativebridge/native_bridge.h"
@@ -436,6 +437,9 @@
}
#endif
+// The list of open zygote file descriptors.
+static FileDescriptorTable* gOpenFdTable = NULL;
+
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
@@ -450,6 +454,22 @@
SetForkLoad(true);
#endif
+ // Close any logging related FDs before we start evaluating the list of
+ // file descriptors.
+ __android_log_close();
+
+ // If this is the first fork for this zygote, create the open FD table.
+ // If it isn't, we just need to check whether the list of open files has
+ // changed (and it shouldn't in the normal case).
+ if (gOpenFdTable == NULL) {
+ gOpenFdTable = FileDescriptorTable::Create();
+ if (gOpenFdTable == NULL) {
+ RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
+ }
+ } else if (!gOpenFdTable->Restat()) {
+ RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
+ }
+
pid_t pid = fork();
if (pid == 0) {
@@ -459,6 +479,12 @@
// Clean up any descriptors which must be closed immediately
DetachDescriptors(env, fdsToClose);
+ // Re-open all remaining open file descriptors so that they aren't shared
+ // with the zygote across a fork.
+ if (!gOpenFdTable->ReopenOrDetach()) {
+ RuntimeAbort(env, __LINE__, "Unable to reopen whitelisted descriptors.");
+ }
+
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(env);
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
new file mode 100644
index 0000000..2b36c9f
--- /dev/null
+++ b/core/jni/fd_utils-inl.h
@@ -0,0 +1,533 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <unordered_map>
+#include <set>
+#include <vector>
+#include <algorithm>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include "JNIHelp.h"
+#include "ScopedPrimitiveArray.h"
+
+// Whitelist of open paths that the zygote is allowed to keep open.
+//
+// In addition to the paths listed here, all files ending with
+// ".jar" under /system/framework" are whitelisted. See
+// FileDescriptorInfo::IsWhitelisted for the canonical definition.
+//
+// If the whitelisted path is associated with a regular file or a
+// character device, the file is reopened after a fork with the same
+// offset and mode. If the whilelisted path is associated with a
+// AF_UNIX socket, the socket will refer to /dev/null after each
+// fork, and all operations on it will fail.
+static const char* kPathWhitelist[] = {
+ "/dev/null",
+ "/dev/pmsg0",
+ "/dev/socket/zygote",
+ "/dev/socket/zygote_secondary",
+ "/system/etc/event-log-tags",
+ "/sys/kernel/debug/tracing/trace_marker",
+ "/system/framework/framework-res.apk",
+ "/dev/urandom",
+ "/dev/ion",
+ "/dev/dri/renderD129", // Fixes b/31172436
+};
+
+static const char* kFdPath = "/proc/self/fd";
+
+// Keeps track of all relevant information (flags, offset etc.) of an
+// open zygote file descriptor.
+class FileDescriptorInfo {
+ public:
+ // Create a FileDescriptorInfo for a given file descriptor. Returns
+ // |NULL| if an error occurred.
+ static FileDescriptorInfo* createFromFd(int fd) {
+ struct stat f_stat;
+ // This should never happen; the zygote should always have the right set
+ // of permissions required to stat all its open files.
+ if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+ ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ if (S_ISSOCK(f_stat.st_mode)) {
+ std::string socket_name;
+ if (!GetSocketName(fd, &socket_name)) {
+ return NULL;
+ }
+
+ if (!IsWhitelisted(socket_name)) {
+ ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+ return NULL;
+ }
+
+ return new FileDescriptorInfo(fd);
+ }
+
+ // We only handle whitelisted regular files and character devices. Whitelisted
+ // character devices must provide a guarantee of sensible behaviour when
+ // reopened.
+ //
+ // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
+ // S_ISLINK : Not supported.
+ // S_ISBLK : Not supported.
+ // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
+ // with the child process across forks but those should have been closed
+ // before we got to this point.
+ if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
+ ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+ return NULL;
+ }
+
+ std::string file_path;
+ if (!Readlink(fd, &file_path)) {
+ return NULL;
+ }
+
+ if (!IsWhitelisted(file_path)) {
+ ALOGE("Not whitelisted : %s", file_path.c_str());
+ return NULL;
+ }
+
+ // File descriptor flags : currently on FD_CLOEXEC. We can set these
+ // using F_SETFD - we're single threaded at this point of execution so
+ // there won't be any races.
+ const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
+ if (fd_flags == -1) {
+ ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ // File status flags :
+ // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
+ // to the open() call.
+ //
+ // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
+ // do about these, since the file has already been created. We shall ignore
+ // them here.
+ //
+ // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
+ // can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
+ // In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
+ // their presence and pass them in to open().
+ int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+ if (fs_flags == -1) {
+ ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ // File offset : Ignore the offset for non seekable files.
+ const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+
+ // We pass the flags that open accepts to open, and use F_SETFL for
+ // the rest of them.
+ static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
+ int open_flags = fs_flags & (kOpenFlags);
+ fs_flags = fs_flags & (~(kOpenFlags));
+
+ return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
+ }
+
+ // Checks whether the file descriptor associated with this object
+ // refers to the same description.
+ bool Restat() const {
+ struct stat f_stat;
+ if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+ return false;
+ }
+
+ return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
+ }
+
+ bool ReopenOrDetach() const {
+ if (is_sock) {
+ return DetachSocket();
+ }
+
+ // NOTE: This might happen if the file was unlinked after being opened.
+ // It's a common pattern in the case of temporary files and the like but
+ // we should not allow such usage from the zygote.
+ const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
+
+ if (new_fd == -1) {
+ ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
+ close(new_fd);
+ ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
+ close(new_fd);
+ ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+ return false;
+ }
+
+ if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
+ close(new_fd);
+ ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+ close(new_fd);
+ ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+ return false;
+ }
+
+ close(new_fd);
+
+ return true;
+ }
+
+ const int fd;
+ const struct stat stat;
+ const std::string file_path;
+ const int open_flags;
+ const int fd_flags;
+ const int fs_flags;
+ const off_t offset;
+ const bool is_sock;
+
+ private:
+ FileDescriptorInfo(int fd) :
+ fd(fd),
+ stat(),
+ open_flags(0),
+ fd_flags(0),
+ fs_flags(0),
+ offset(0),
+ is_sock(true) {
+ }
+
+ FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
+ int fd_flags, int fs_flags, off_t offset) :
+ fd(fd),
+ stat(stat),
+ file_path(file_path),
+ open_flags(open_flags),
+ fd_flags(fd_flags),
+ fs_flags(fs_flags),
+ offset(offset),
+ is_sock(false) {
+ }
+
+ // Returns true iff. a given path is whitelisted. A path is whitelisted
+ // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
+ // under /system/framework that ends with ".jar".
+ static bool IsWhitelisted(const std::string& path) {
+ for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
+ if (kPathWhitelist[i] == path) {
+ return true;
+ }
+ }
+
+ static const std::string kFrameworksPrefix = "/system/framework/";
+ static const std::string kJarSuffix = ".jar";
+ if (path.compare(0, kFrameworksPrefix.size(), kFrameworksPrefix) == 0 &&
+ path.compare(path.size() - kJarSuffix.size(), kJarSuffix.size(), kJarSuffix) == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ // TODO: Call android::base::Readlink instead of copying the code here.
+ static bool Readlink(const int fd, std::string* result) {
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+ // Code copied from android::base::Readlink starts here :
+
+ // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
+ // and truncates to whatever size you do supply, so it can't be used to query.
+ // We could call lstat first, but that would introduce a race condition that
+ // we couldn't detect.
+ // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
+ char buf[4096];
+ ssize_t len = readlink(path, buf, sizeof(buf));
+ if (len == -1) return false;
+
+ result->assign(buf, len);
+ return true;
+ }
+
+ // Returns the locally-bound name of the socket |fd|. Returns true
+ // iff. all of the following hold :
+ //
+ // - the socket's sa_family is AF_UNIX.
+ // - the length of the path is greater than zero (i.e, not an unnamed socket).
+ // - the first byte of the path isn't zero (i.e, not a socket with an abstract
+ // address).
+ static bool GetSocketName(const int fd, std::string* result) {
+ sockaddr_storage ss;
+ sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
+ socklen_t addr_len = sizeof(ss);
+
+ if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
+ ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+ return false;
+ }
+
+ if (addr->sa_family != AF_UNIX) {
+ ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+ return false;
+ }
+
+ const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
+
+ size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
+ // This is an unnamed local socket, we do not accept it.
+ if (path_len == 0) {
+ ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+ return false;
+ }
+
+ // This is a local socket with an abstract address, we do not accept it.
+ if (unix_addr->sun_path[0] == '\0') {
+ ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+ return false;
+ }
+
+ // If we're here, sun_path must refer to a null terminated filesystem
+ // pathname (man 7 unix). Remove the terminator before assigning it to an
+ // std::string.
+ if (unix_addr->sun_path[path_len - 1] == '\0') {
+ --path_len;
+ }
+
+ result->assign(unix_addr->sun_path, path_len);
+ return true;
+ }
+
+ bool DetachSocket() const {
+ const int dev_null_fd = open("/dev/null", O_RDWR);
+ if (dev_null_fd < 0) {
+ ALOGE("Failed to open /dev/null : %s", strerror(errno));
+ return false;
+ }
+
+ if (dup2(dev_null_fd, fd) == -1) {
+ ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+ return false;
+ }
+
+ if (close(dev_null_fd) == -1) {
+ ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+ return false;
+ }
+
+ return true;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
+};
+
+// A FileDescriptorTable is a collection of FileDescriptorInfo objects
+// keyed by their FDs.
+class FileDescriptorTable {
+ public:
+ // Creates a new FileDescriptorTable. This function scans
+ // /proc/self/fd for the list of open file descriptors and collects
+ // information about them. Returns NULL if an error occurs.
+ static FileDescriptorTable* Create() {
+ DIR* d = opendir(kFdPath);
+ if (d == NULL) {
+ ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+ return NULL;
+ }
+ int dir_fd = dirfd(d);
+ dirent* e;
+
+ std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
+ while ((e = readdir(d)) != NULL) {
+ const int fd = ParseFd(e, dir_fd);
+ if (fd == -1) {
+ continue;
+ }
+
+ FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
+ if (info == NULL) {
+ if (closedir(d) == -1) {
+ ALOGE("Unable to close directory : %s", strerror(errno));
+ }
+ return NULL;
+ }
+ open_fd_map[fd] = info;
+ }
+
+ if (closedir(d) == -1) {
+ ALOGE("Unable to close directory : %s", strerror(errno));
+ return NULL;
+ }
+ return new FileDescriptorTable(open_fd_map);
+ }
+
+ bool Restat() {
+ std::set<int> open_fds;
+
+ // First get the list of open descriptors.
+ DIR* d = opendir(kFdPath);
+ if (d == NULL) {
+ ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+ return false;
+ }
+
+ int dir_fd = dirfd(d);
+ dirent* e;
+ while ((e = readdir(d)) != NULL) {
+ const int fd = ParseFd(e, dir_fd);
+ if (fd == -1) {
+ continue;
+ }
+
+ open_fds.insert(fd);
+ }
+
+ if (closedir(d) == -1) {
+ ALOGE("Unable to close directory : %s", strerror(errno));
+ return false;
+ }
+
+ return RestatInternal(open_fds);
+ }
+
+ // Reopens all file descriptors that are contained in the table. Returns true
+ // if all descriptors were successfully re-opened or detached, and false if an
+ // error occurred.
+ bool ReopenOrDetach() {
+ std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
+ for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
+ const FileDescriptorInfo* info = it->second;
+ if (info == NULL || !info->ReopenOrDetach()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
+ : open_fd_map_(map) {
+ }
+
+ bool RestatInternal(std::set<int>& open_fds) {
+ bool error = false;
+
+ // Iterate through the list of file descriptors we've already recorded
+ // and check whether :
+ //
+ // (a) they continue to be open.
+ // (b) they refer to the same file.
+ std::unordered_map<int, FileDescriptorInfo*>::iterator it;
+ for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
+ std::set<int>::const_iterator element = open_fds.find(it->first);
+ if (element == open_fds.end()) {
+ // The entry from the file descriptor table is no longer in the list
+ // of open files. We warn about this condition and remove it from
+ // the list of FDs under consideration.
+ //
+ // TODO(narayan): This will be an error in a future android release.
+ // error = true;
+ // ALOGW("Zygote closed file descriptor %d.", it->first);
+ open_fd_map_.erase(it);
+ } else {
+ // The entry from the file descriptor table is still open. Restat
+ // it and check whether it refers to the same file.
+ open_fds.erase(element);
+ const bool same_file = it->second->Restat();
+ if (!same_file) {
+ // The file descriptor refers to a different description. We must
+ // update our entry in the table.
+ delete it->second;
+ it->second = FileDescriptorInfo::createFromFd(*element);
+ if (it->second == NULL) {
+ // The descriptor no longer no longer refers to a whitelisted file.
+ // We flag an error and remove it from the list of files we're
+ // tracking.
+ error = true;
+ open_fd_map_.erase(it);
+ }
+ } else {
+ // It's the same file. Nothing to do here.
+ }
+ }
+ }
+
+ if (open_fds.size() > 0) {
+ // The zygote has opened new file descriptors since our last inspection.
+ // We warn about this condition and add them to our table.
+ //
+ // TODO(narayan): This will be an error in a future android release.
+ // error = true;
+ // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
+
+ // TODO(narayan): This code will be removed in a future android release.
+ std::set<int>::const_iterator it;
+ for (it = open_fds.begin(); it != open_fds.end(); ++it) {
+ const int fd = (*it);
+ FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
+ if (info == NULL) {
+ // A newly opened file is not on the whitelist. Flag an error and
+ // continue.
+ error = true;
+ } else {
+ // Track the newly opened file.
+ open_fd_map_[fd] = info;
+ }
+ }
+ }
+
+ return !error;
+ }
+
+ static int ParseFd(dirent* e, int dir_fd) {
+ char* end;
+ const int fd = strtol(e->d_name, &end, 10);
+ if ((*end) != '\0') {
+ return -1;
+ }
+
+ // Don't bother with the standard input/output/error, they're handled
+ // specially post-fork anyway.
+ if (fd <= STDERR_FILENO || fd == dir_fd) {
+ return -1;
+ }
+
+ return fd;
+ }
+
+ // Invariant: All values in this unordered_map are non-NULL.
+ std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
+};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ed71fc2f..6c6fd90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -183,6 +183,7 @@
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
<protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
<protected-broadcast android:name="android.btopp.intent.action.LIST" />
@@ -199,6 +200,8 @@
<protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" />
<protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />
<protected-broadcast android:name="com.android.bluetooth.pbap.authcancelled" />
+ <protected-broadcast android:name="com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT" />
+ <protected-broadcast android:name="com.android.bluetooth.sap.action.DISCONNECT_ACTION" />
<protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
@@ -394,6 +397,8 @@
<protected-broadcast android:name="android.bluetooth.adapter.action.BLE_STATE_CHANGED" />
<protected-broadcast android:name="com.android.bluetooth.map.USER_CONFIRM_TIMEOUT" />
+ <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
+ <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast android:name="android.content.jobscheduler.JOB_DELAY_EXPIRED" />
<protected-broadcast android:name="android.content.syncmanager.SYNC_ALARM" />
<protected-broadcast android:name="android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION" />
@@ -1308,6 +1313,7 @@
android:protectionLevel="dangerous"
android:description="@string/permdesc_getAccounts"
android:label="@string/permlab_getAccounts" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- @SystemApi Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
@@ -1628,7 +1634,7 @@
<!-- @hide Allows an application to create, remove users and get the list of
users on the device. Applications holding this permission can only create restricted,
- guest, managed, and ephemeral users. For creating other kind of users,
+ guest, managed, demo, and ephemeral users. For creating other kind of users,
{@link android.Manifest.permission#MANAGE_USERS} is needed.
This permission is not available to third party applications. -->
<permission android:name="android.permission.CREATE_USERS"
diff --git a/core/res/res/drawable/ic_refresh.xml b/core/res/res/drawable/ic_refresh.xml
index 1f67168..1297407 100644
--- a/core/res/res/drawable/ic_refresh.xml
+++ b/core/res/res/drawable/ic_refresh.xml
@@ -21,7 +21,4 @@
<path
android:fillColor="#FF000000"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4.0 12.0,4.0c-4.42,0.0 -7.99,3.58 -7.99,8.0s3.57,8.0 7.99,8.0c3.73,0.0 6.84,-2.55 7.73,-6.0l-2.08,0.0c-0.82,2.33 -3.04,4.0 -5.65,4.0 -3.31,0.0 -6.0,-2.69 -6.0,-6.0s2.69,-6.0 6.0,-6.0c1.66,0.0 3.1,0.69 4.22,1.78L13.0,11.0l7.0,0.0L20.0,4.0l-2.35,2.35z"/>
- <path
- android:pathData="M0 0h24v24H0z"
- android:fillColor="#00000000"/>
</vector>
diff --git a/core/res/res/drawable/progress_indeterminate_anim_large_material.xml b/core/res/res/drawable/progress_indeterminate_anim_large_material.xml
new file mode 100644
index 0000000..560ec5af
--- /dev/null
+++ b/core/res/res/drawable/progress_indeterminate_anim_large_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/vector_drawable_progress_bar_large" >
+ <target
+ android:name="progressBar"
+ android:animation="@anim/progress_indeterminate_material" />
+
+ <target
+ android:name="root"
+ android:animation="@anim/progress_indeterminate_rotation_material" />
+</animated-vector>
+
diff --git a/core/res/res/drawable/progress_indeterminate_anim_medium_material.xml b/core/res/res/drawable/progress_indeterminate_anim_medium_material.xml
new file mode 100644
index 0000000..fbea22f
--- /dev/null
+++ b/core/res/res/drawable/progress_indeterminate_anim_medium_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/vector_drawable_progress_bar_medium" >
+
+ <target
+ android:name="progressBar"
+ android:animation="@anim/progress_indeterminate_material" />
+
+ <target
+ android:name="root"
+ android:animation="@anim/progress_indeterminate_rotation_material" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_large_material.xml b/core/res/res/drawable/progress_large_material.xml
index 526f914..ee82e35 100644
--- a/core/res/res/drawable/progress_large_material.xml
+++ b/core/res/res/drawable/progress_large_material.xml
@@ -13,16 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/vector_drawable_progress_bar_large" >
-
- <target
- android:name="progressBar"
- android:animation="@anim/progress_indeterminate_material" />
-
- <target
- android:name="root"
- android:animation="@anim/progress_indeterminate_rotation_material" />
-
-</animated-vector>
+<com.android.internal.graphics.drawable.AnimationScaleListDrawable
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/progress_static_material" />
+ <item android:drawable="@drawable/progress_indeterminate_anim_large_material" />
+</com.android.internal.graphics.drawable.AnimationScaleListDrawable>
diff --git a/core/res/res/drawable/progress_medium_material.xml b/core/res/res/drawable/progress_medium_material.xml
index cc35816..5c92600 100644
--- a/core/res/res/drawable/progress_medium_material.xml
+++ b/core/res/res/drawable/progress_medium_material.xml
@@ -13,15 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/vector_drawable_progress_bar_medium" >
-
- <target
- android:name="progressBar"
- android:animation="@anim/progress_indeterminate_material" />
-
- <target
- android:name="root"
- android:animation="@anim/progress_indeterminate_rotation_material" />
-
-</animated-vector>
\ No newline at end of file
+<com.android.internal.graphics.drawable.AnimationScaleListDrawable
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/progress_static_material" />
+ <item android:drawable="@drawable/progress_indeterminate_anim_medium_material" />
+</com.android.internal.graphics.drawable.AnimationScaleListDrawable>
diff --git a/core/res/res/drawable/progress_small_material.xml b/core/res/res/drawable/progress_small_material.xml
index c6e4380..cec9d95 100644
--- a/core/res/res/drawable/progress_small_material.xml
+++ b/core/res/res/drawable/progress_small_material.xml
@@ -13,16 +13,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/vector_drawable_progress_bar_small" >
-
- <target
- android:name="progressBar"
- android:animation="@anim/progress_indeterminate_material" />
-
- <target
- android:name="root"
- android:animation="@anim/progress_indeterminate_rotation_material" />
-
-</animated-vector>
+<com.android.internal.graphics.drawable.AnimationScaleListDrawable
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/progress_static_material" />
+ <item>
+ <animated-vector android:drawable="@drawable/vector_drawable_progress_bar_small" >
+ <target
+ android:name="progressBar"
+ android:animation="@anim/progress_indeterminate_material" />
+ <target
+ android:name="root"
+ android:animation="@anim/progress_indeterminate_rotation_material" />
+ </animated-vector>
+ </item>
+</com.android.internal.graphics.drawable.AnimationScaleListDrawable>
diff --git a/core/res/res/drawable/progress_static_material.xml b/core/res/res/drawable/progress_static_material.xml
new file mode 100644
index 0000000..b078fa9
--- /dev/null
+++ b/core/res/res/drawable/progress_static_material.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ >
+ <path
+ android:fillColor="?attr/colorControlActivated"
+ android:pathData="M17.65,6.35C16.2,4.9 14.21,4.0 12.0,4.0c-4.42,0.0 -7.99,3.58 -7.99,8.0s3.57,8.0 7.99,8.0c3.73,0.0 6.84,-2.55 7.73,-6.0l-2.08,0.0c-0.82,2.33 -3.04,4.0 -5.65,4.0 -3.31,0.0 -6.0,-2.69 -6.0,-6.0s2.69,-6.0 6.0,-6.0c1.66,0.0 3.1,0.69 4.22,1.78L13.0,11.0l7.0,0.0L20.0,4.0l-2.35,2.35z"/>
+</vector>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 8023edf..350caaa 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -598,7 +598,7 @@
<string name="phoneTypeCallback" msgid="2712175203065678206">"Devolver chamada"</string>
<string name="phoneTypeCar" msgid="8738360689616716982">"Coche"</string>
<string name="phoneTypeCompanyMain" msgid="540434356461478916">"Empresa (ppal.)"</string>
- <string name="phoneTypeIsdn" msgid="8022453193171370337">"ISDN"</string>
+ <string name="phoneTypeIsdn" msgid="8022453193171370337">"RDSI"</string>
<string name="phoneTypeMain" msgid="6766137010628326916">"Principal"</string>
<string name="phoneTypeOtherFax" msgid="8587657145072446565">"Outro fax"</string>
<string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index bebe027..10c5836 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1362,7 +1362,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"אפשרויות נוספות"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"אחסון משותף פנימי"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"אחסון שיתוף פנימי"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"כרטיס SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"כרטיס SD של <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"כונן USB"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5b446b4..8bd7160 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1310,7 +1310,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"옵션 더보기"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"내부 공유 저장공간"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"내부 공유 저장용량"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 카드"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 카드"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB 드라이브"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 0f82dcb..58c2c8a 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1197,8 +1197,7 @@
<string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Колдонмого орнотуу сеанстарын окуу мүмкүнчүлүгүн берет. Ушуну менен, ал жигердүү топтом орнотууларынын чоо-жайын көрө алат."</string>
<string name="permlab_requestInstallPackages" msgid="5782013576218172577">"орнотуу топтомдорун суроо"</string>
<string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Колдонмо топтомдорду орнотууга уруксат сурай алат."</string>
- <!-- no translation found for tutorial_double_tap_to_zoom_message_short (1311810005957319690) -->
- <skip />
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Масштабдын параметрлерин өзгөртүү үчүн бул жерди эки жолу басыңыз."</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Виджетти кошуу мүмкүн болбоду."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Өтүү"</string>
<string name="ime_action_search" msgid="658110271822807811">"Издөө"</string>
@@ -1229,10 +1228,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Эскертмелердин маанилүүлүгүн баалоо кызматы"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN иштетилди"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> аркылуу жандырылды"</string>
- <!-- no translation found for vpn_text (1610714069627824309) -->
- <skip />
- <!-- no translation found for vpn_text_long (4907843483284977618) -->
- <skip />
+ <string name="vpn_text" msgid="1610714069627824309">"Тармактын параметрлерин өзгөртүү үчүн бул жерди басыңыз."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"<xliff:g id="SESSION">%s</xliff:g> сеансына туташуу ишке ашты. Желенин параметрлерин өзгөртүү үчүн бул жерди басыңыз."</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Дайым иштеген VPN туташууда…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Дайым иштеген VPN туташтырылды"</string>
<string name="vpn_lockdown_error" msgid="6009249814034708175">"Дайым иштеген VPN\'де ката кетти"</string>
diff --git a/core/res/res/values-mcc001/config.xml b/core/res/res/values-mcc001/config.xml
new file mode 100644
index 0000000..93cde03
--- /dev/null
+++ b/core/res/res/values-mcc001/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <bool name="config_use_sim_language_file">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc222-mnc10/config.xml b/core/res/res/values-mcc222-mnc10/config.xml
index cd6e8c6..c819de2 100644
--- a/core/res/res/values-mcc222-mnc10/config.xml
+++ b/core/res/res/values-mcc222-mnc10/config.xml
@@ -28,29 +28,4 @@
<string-array translatable="false" name="config_tether_apndata">
<item>Tethering Internet,web.omnitel.it,,,,,,,,,222,10,,DUN</item>
</string-array>
-
- <string-array translatable="false" name="config_operatorConsideredNonRoaming">
- <item>21401</item>
- <item>21402</item>
- <item>21403</item>
- <item>21404</item>
- <item>21405</item>
- <item>21406</item>
- <item>21407</item>
- <item>21408</item>
- <item>21409</item>
- <item>21410</item>
- <item>21411</item>
- <item>21412</item>
- <item>21413</item>
- <item>21414</item>
- <item>21415</item>
- <item>21416</item>
- <item>21417</item>
- <item>21418</item>
- <item>21419</item>
- <item>21420</item>
- <item>21421</item>
- </string-array>
-
</resources>
diff --git a/core/res/res/values-mcc232-mnc10/config.xml b/core/res/res/values-mcc232-mnc10/config.xml
new file mode 100644
index 0000000..bdf83016
--- /dev/null
+++ b/core/res/res/values-mcc232-mnc10/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23203</item>
+ <item>23205</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc232-mnc13/config.xml b/core/res/res/values-mcc232-mnc13/config.xml
new file mode 100644
index 0000000..2c14f87
--- /dev/null
+++ b/core/res/res/values-mcc232-mnc13/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23203</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc500/config.xml b/core/res/res/values-mcc302-mnc500/config.xml
new file mode 100644
index 0000000..77f6419
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc500/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc510/config.xml b/core/res/res/values-mcc302-mnc510/config.xml
new file mode 100644
index 0000000..77f6419
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc510/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index ae115d8..5a356d5 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1310,7 +1310,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno partilhado"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Armazen. interno partilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidade USB"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 48e4201..0a96ba3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5313,6 +5313,21 @@
<attr name="alpha" />
</declare-styleable>
+ <!-- Drawable used to render according to the animation scale. Esp. when it is 0 due to battery
+ saver mode. It should contain one animatable drawable and one static drawable.
+ @hide -->
+ <declare-styleable name="AnimationScaleListDrawable">
+ </declare-styleable>
+
+ <!-- Attributes that can be assigned to a AnimationScaleListDrawable item.
+ @hide -->
+ <declare-styleable name="AnimationScaleListDrawableItem">
+ <!-- Reference to a drawable resource to use for the state. If not
+ given, the drawable must be defined by the first child tag. -->
+ <attr name="drawable" />
+ </declare-styleable>
+
+
<!-- Drawable used to render a geometric shape, with a gradient or a solid color. -->
<declare-styleable name="GradientDrawable">
<!-- Indicates whether the drawable should intially be visible. -->
@@ -5342,7 +5357,10 @@
<attr name="innerRadius" format="dimension" />
<!-- Thickness of the ring. When defined, thicknessRatio is ignored. -->
<attr name="thickness" format="dimension" />
- <!-- Indicates whether the drawable's level affects the way the gradient is drawn. -->
+ <!-- Whether the drawable level value (see
+ {@link android.graphics.drawable.Drawable#getLevel()}) is used to scale the shape.
+ Scaling behavior depends on the shape type. For "ring", the angle is scaled from 0 to
+ 360. For all other types, there is no effect. The default value is true. -->
<attr name="useLevel" />
<!-- If set, specifies the color to apply to the drawable as a tint. By default,
no tint is applied. May be a color state list. -->
@@ -5376,28 +5394,37 @@
<declare-styleable name="GradientDrawableGradient">
<!-- Start color of the gradient. -->
<attr name="startColor" format="color" />
- <!-- Optional center color. For linear gradients, use centerX or centerY
- to place the center color. -->
+ <!-- Optional center color. For linear gradients, use centerX or centerY to place the center
+ color. -->
<attr name="centerColor" format="color" />
<!-- End color of the gradient. -->
<attr name="endColor" format="color" />
+ <!-- Whether the drawable level value (see
+ {@link android.graphics.drawable.Drawable#getLevel()}) is used to scale the gradient.
+ Scaling behavior varies based on gradient type. For "linear", adjusts the ending
+ position along the gradient's axis of orientation. For "radial", adjusts the outer
+ radius. For "sweep", adjusts the ending angle. The default value is false. -->
<attr name="useLevel" format="boolean" />
- <!-- Angle of the gradient. -->
+ <!-- Angle of the gradient, used only with linear gradient. Must be a multiple of 45 in the
+ range [0, 315]. -->
<attr name="angle" format="float" />
<!-- Type of gradient. The default type is linear. -->
<attr name="type">
- <!-- Linear gradient. -->
+ <!-- Linear gradient extending across the center point. -->
<enum name="linear" value="0" />
- <!-- Radial, or circular, gradient. -->
+ <!-- Radial gradient extending from the center point outward. -->
<enum name="radial" value="1" />
- <!-- Sweep, or angled or diamond, gradient. -->
+ <!-- Sweep (or angular) gradient sweeping counter-clockwise around the center point. -->
<enum name="sweep" value="2" />
</attr>
- <!-- X coordinate of the origin of the gradient within the shape. -->
+ <!-- X-position of the center point of the gradient within the shape as a fraction of the
+ width. The default value is 0.5. -->
<attr name="centerX" format="float|fraction" />
- <!-- Y coordinate of the origin of the gradient within the shape. -->
+ <!-- Y-position of the center point of the gradient within the shape as a fraction of the
+ height. The default value is 0.5. -->
<attr name="centerY" format="float|fraction" />
- <!-- Radius of the gradient, used only with radial gradient. -->
+ <!-- Radius of the gradient, used only with radial gradient. May be an explicit dimension
+ or a fractional value relative to the shape's minimum dimension. -->
<attr name="gradientRadius" format="float|fraction|dimension" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b80b0a7..55a87ee 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2153,6 +2153,11 @@
<!-- Flag specifying whether VT is available on device -->
<bool name="config_device_vt_available">false</bool>
+ <!-- Flag specifying whether the device will use the "allow_hold_in_ims_call" carrier config
+ option. When false, the device will support holding of IMS calls, regardless of the
+ carrier config setting. -->
+ <bool name="config_device_respects_hold_carrier_config">true</bool>
+
<!-- Flag specifying whether VT should be available for carrier: independent of
carrier provisioning. If false: hard disabled. If true: then depends on carrier
provisioning, availability etc -->
@@ -2295,8 +2300,8 @@
<!-- An array of CDMA roaming indicators which means international roaming -->
<integer-array translatable="false" name="config_cdma_international_roaming_indicators" />
- <!-- set the system language as value of EF LI/EF PL -->
- <bool name="config_use_sim_language_file">true</bool>
+ <!-- flag to indicate if EF LI/EF PL should be used for system language -->
+ <bool name="config_use_sim_language_file">false</bool>
<!-- Use ERI text for network name on CDMA LTE -->
<bool name="config_LTE_eri_for_network_name">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 924d338..67ab8cb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1942,6 +1942,7 @@
<java-symbol type="anim" name="lock_screen_behind_enter_fade_in" />
<java-symbol type="anim" name="lock_screen_wallpaper_exit" />
<java-symbol type="anim" name="launch_task_behind_source" />
+ <java-symbol type="anim" name="wallpaper_open_exit" />
<java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
<java-symbol type="dimen" name="status_bar_icon_size" />
@@ -2211,6 +2212,7 @@
<java-symbol type="bool" name="config_carrier_volte_provisioned" />
<java-symbol type="bool" name="config_carrier_volte_tty_supported" />
<java-symbol type="bool" name="config_device_vt_available" />
+ <java-symbol type="bool" name="config_device_respects_hold_carrier_config" />
<java-symbol type="bool" name="config_carrier_vt_available" />
<java-symbol type="bool" name="config_device_wfc_ims_available" />
<java-symbol type="bool" name="config_carrier_wfc_ims_available" />
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 5c54324..c386108 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -77,6 +77,8 @@
private long mProducer;
private long mFrameAvailableListener;
+ private boolean mIsSingleBuffered;
+
/**
* Callback interface for being notified that a new stream frame is available.
*/
@@ -130,6 +132,7 @@
*/
public SurfaceTexture(int texName, boolean singleBufferMode) {
mCreatorLooper = Looper.myLooper();
+ mIsSingleBuffered = singleBufferMode;
nativeInit(false, texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}
@@ -157,6 +160,7 @@
*/
public SurfaceTexture(boolean singleBufferMode) {
mCreatorLooper = Looper.myLooper();
+ mIsSingleBuffered = singleBufferMode;
nativeInit(true, 0, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}
@@ -378,6 +382,14 @@
}
}
+ /**
+ * Returns true if the SurfaceTexture is single-buffered
+ * @hide
+ */
+ public boolean isSingleBuffered() {
+ return mIsSingleBuffered;
+ }
+
private native void nativeInit(boolean isDetached, int texName,
boolean singleBufferMode, WeakReference<SurfaceTexture> weakSelf)
throws Surface.OutOfResourcesException;
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index c836204..0f305f3 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -144,6 +144,55 @@
* android:valueType="pathType"/>
* </set>
* </pre></li>
+ * <p>
+ * Since AAPT tool is now supporting a new format which can bundle several related XML files into
+ * one, we can merge the previous example into one XML file, like this:
+ * </p>
+ * <pre>
+ * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ * <aapt:attr name="android:drawable">
+ * <vector
+ * android:height="64dp"
+ * android:width="64dp"
+ * android:viewportHeight="600"
+ * android:viewportWidth="600" >
+ * <group
+ * android:name="rotationGroup"
+ * android:pivotX="300.0"
+ * android:pivotY="300.0"
+ * android:rotation="45.0" >
+ * <path
+ * android:name="v"
+ * android:fillColor="#000000"
+ * android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+ * </group>
+ * </vector>
+ * </aapt:attr>
+ *
+ * <target android:name="rotationGroup"> *
+ * <aapt:attr name="android:animation">
+ * <objectAnimator
+ * android:duration="6000"
+ * android:propertyName="rotation"
+ * android:valueFrom="0"
+ * android:valueTo="360" />
+ * </aapt:attr>
+ * </target>
+ *
+ * <target android:name="v" >
+ * <aapt:attr name="android:animation">
+ * <set>
+ * <objectAnimator
+ * android:duration="3000"
+ * android:propertyName="pathData"
+ * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
+ * android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
+ * android:valueType="pathType"/>
+ * </set>
+ * </aapt:attr>
+ * </target>
+ * </animated-vector>
+ * </pre>
*
* @attr ref android.R.styleable#AnimatedVectorDrawable_drawable
* @attr ref android.R.styleable#AnimatedVectorDrawableTarget_name
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 7f3a437..3aca867 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1422,9 +1422,10 @@
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
+ * @hide
*/
- static @NonNull TypedArray obtainAttributes(@NonNull Resources res, @Nullable Theme theme,
- @NonNull AttributeSet set, @NonNull int[] attrs) {
+ protected static @NonNull TypedArray obtainAttributes(@NonNull Resources res,
+ @Nullable Theme theme, @NonNull AttributeSet set, @NonNull int[] attrs) {
if (theme == null) {
return res.obtainAttributes(set, attrs);
}
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index cc7f5c7..c7a3c75 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -27,8 +27,8 @@
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.LayoutDirection;
@@ -601,8 +601,9 @@
* during inflation.
*
* @param res the resources used to inflate density-dependent values
+ * @hide
*/
- final void updateDensity(Resources res) {
+ protected final void updateDensity(Resources res) {
mDrawableContainerState.updateDensity(res);
}
@@ -711,7 +712,10 @@
boolean mHasTintList;
boolean mHasTintMode;
- DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
+ /**
+ * @hide
+ */
+ protected DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
mSourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 3dbd2a9..8c633b0 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -475,16 +475,17 @@
}
/**
- * Sets the center location in pixels of the gradient. The radius is
- * honored only when the gradient type is set to {@link #RADIAL_GRADIENT}
- * or {@link #SWEEP_GRADIENT}.
+ * Sets the position of the center of the gradient as a fraction of the
+ * width and height.
+ * <p>
+ * The default value is (0.5, 0.5).
* <p>
* <strong>Note</strong>: changing this property will affect all instances
* of a drawable loaded from a resource. It is recommended to invoke
* {@link #mutate()} before changing this property.
*
- * @param x the x coordinate of the gradient's center in pixels
- * @param y the y coordinate of the gradient's center in pixels
+ * @param x the X-position of the center of the gradient
+ * @param y the Y-position of the center of the gradient
*
* @see #mutate()
* @see #setGradientType(int)
@@ -498,9 +499,10 @@
}
/**
- * Returns the center X location of this gradient in pixels.
+ * Returns the X-position of the center of the gradient as a fraction of
+ * the width.
*
- * @return the center X location of this gradient in pixels
+ * @return the X-position of the center of the gradient
* @see #setGradientCenter(float, float)
*/
public float getGradientCenterX() {
@@ -508,9 +510,10 @@
}
/**
- * Returns the center Y location of this gradient in pixels.
+ * Returns the Y-position of the center of this gradient as a fraction of
+ * the height.
*
- * @return the center Y location of this gradient in pixels
+ * @return the Y-position of the center of the gradient
* @see #setGradientCenter(float, float)
*/
public float getGradientCenterY() {
@@ -554,19 +557,43 @@
}
/**
- * Sets whether or not this drawable will honor its {@code level} property.
+ * Sets whether this drawable's {@code level} property will be used to
+ * scale the gradient. If a gradient is not used, this property has no
+ * effect.
* <p>
- * <strong>Note</strong>: changing this property will affect all instances
+ * Scaling behavior varies based on gradient type:
+ * <ul>
+ * <li>{@link #LINEAR_GRADIENT} adjusts the ending position along the
+ * gradient's axis of orientation (see {@link #getOrientation()})
+ * <li>{@link #RADIAL_GRADIENT} adjusts the outer radius
+ * <li>{@link #SWEEP_GRADIENT} adjusts the ending angle
+ * <ul>
+ * <p>
+ * The default value for this property is {@code false}.
+ * <p>
+ * <strong>Note</strong>: This property corresponds to the
+ * {@code android:useLevel} attribute on the inner {@code <gradient>}
+ * tag, NOT the {@code android:useLevel} attribute on the outer
+ * {@code <shape>} tag. For example,
+ * <pre>{@code
+ * <shape ...>
+ * <gradient
+ * ...
+ * android:useLevel="true" />
+ * </shape>
+ * }</pre><p>
+ * <strong>Note</strong>: Changing this property will affect all instances
* of a drawable loaded from a resource. It is recommended to invoke
* {@link #mutate()} before changing this property.
*
- * @param useLevel {@code true} if this drawable should honor its level,
- * {@code false} otherwise
+ * @param useLevel {@code true} if the gradient should be scaled based on
+ * level, {@code false} otherwise
*
* @see #mutate()
* @see #setLevel(int)
* @see #getLevel()
* @see #getUseLevel()
+ * @attr ref android.R.styleable#GradientDrawableGradient_useLevel
*/
public void setUseLevel(boolean useLevel) {
mGradientState.mUseLevel = useLevel;
@@ -575,12 +602,13 @@
}
/**
- * Returns whether or not this drawable will honor its {@code level}
- * property.
+ * Returns whether this drawable's {@code level} property will be used to
+ * scale the gradient.
*
- * @return {@code true} if this drawable should honor its level,
+ * @return {@code true} if the gradient should be scaled based on level,
* {@code false} otherwise
* @see #setUseLevel(boolean)
+ * @attr ref android.R.styleable#GradientDrawableGradient_useLevel
*/
public boolean getUseLevel() {
return mGradientState.mUseLevel;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 1864206..c30c4c2 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -269,7 +269,8 @@
// If the layer doesn't have a drawable or unresolved theme
// attribute for a drawable, attempt to parse one from the child
- // element.
+ // element. If multiple child elements exist, we'll only use the
+ // first one.
if (layer.mDrawable == null && (layer.mThemeAttrs == null ||
layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) {
while ((type = parser.next()) == XmlPullParser.TEXT) {
@@ -279,13 +280,12 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
- }
- if (layer.mDrawable != null) {
+ // We found a child drawable. Take ownership.
+ layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
+ layer.mDrawable.setCallback(this);
state.mChildrenChangingConfigurations |=
layer.mDrawable.getChangingConfigurations();
- layer.mDrawable.setCallback(this);
}
addLayer(layer);
@@ -387,7 +387,19 @@
final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
if (dr != null) {
+ if (layer.mDrawable != null) {
+ // It's possible that a drawable was already set, in which case
+ // we should clear the callback. We may have also integrated the
+ // drawable's changing configurations, but we don't have enough
+ // information to revert that change.
+ layer.mDrawable.setCallback(null);
+ }
+
+ // Take ownership of the new drawable.
layer.mDrawable = dr;
+ layer.mDrawable.setCallback(this);
+ state.mChildrenChangingConfigurations |=
+ layer.mDrawable.getChangingConfigurations();
}
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index dc1d18f..9ff6965 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -27,9 +27,9 @@
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
-import android.graphics.PorterDuff.Mode;
import android.graphics.Shader;
import android.util.ArrayMap;
import android.util.AttributeSet;
@@ -140,12 +140,16 @@
* in the SVG's path data. This is defined in the viewport space.</dd>
* <dt><code>android:fillColor</code></dt>
* <dd>Specifies the color used to fill the path. May be a color or, for SDK 24+, a color state list
- * or a gradient color. If this property is animated, any value set by the animation will
- * override the original value. No path fill is drawn if this property is not specified.</dd>
+ * or a gradient color (See {@link android.R.styleable#GradientColor}
+ * and {@link android.R.styleable#GradientColorItem}).
+ * If this property is animated, any value set by the animation will override the original value.
+ * No path fill is drawn if this property is not specified.</dd>
* <dt><code>android:strokeColor</code></dt>
* <dd>Specifies the color used to draw the path outline. May be a color or, for SDK 24+, a color
- * state list or a gradient color. If this property is animated, any value set by the animation will
- * override the original value. No path outline is drawn if this property is not specified.</dd>
+ * state list or a gradient color (See {@link android.R.styleable#GradientColor}
+ * and {@link android.R.styleable#GradientColorItem}).
+ * If this property is animated, any value set by the animation will override the original value.
+ * No path outline is drawn if this property is not specified.</dd>
* <dt><code>android:strokeWidth</code></dt>
* <dd>The width a path stroke.</dd>
* <dt><code>android:strokeAlpha</code></dt>
@@ -166,8 +170,9 @@
* <dt><code>android:strokeMiterLimit</code></dt>
* <dd>Sets the Miter limit for a stroked path.</dd>
* <dt><code>android:fillType</code></dt>
- * <dd>Sets the fillType for a path. It is the same as SVG's "fill-rule" properties.
- * For more details, see https://www.w3.org/TR/SVG/painting.html#FillRuleProperty</dd>
+ * <dd>Sets the fillType for a path. The types can be either "evenOdd" or "nonZero". They behave the
+ * same as SVG's "fill-rule" properties. For more details, see
+ * <a href="https://www.w3.org/TR/SVG/painting.html#FillRuleProperty">FillRuleProperty</a></dd>
* </dl></dd>
* </dl>
*
@@ -201,7 +206,26 @@
* android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
* </group>
* </vector>
- * </pre></li>
+ * </pre>
+ * </li>
+ * <li>And here is an example of linear gradient color, which is supported in SDK 24+.
+ * See more details in {@link android.R.styleable#GradientColor} and
+ * {@link android.R.styleable#GradientColorItem}.
+ * <pre>
+ * <gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:angle="90"
+ * android:startColor="?android:attr/colorPrimary"
+ * android:endColor="?android:attr/colorControlActivated"
+ * android:centerColor="#f00"
+ * android:startX="0"
+ * android:startY="0"
+ * android:endX="100"
+ * android:endY="100"
+ * android:type="linear">
+ * </gradient>
+ * </pre>
+ * </li>
+ *
*/
public class VectorDrawable extends Drawable {
diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java
index 2b70b6a..cd1f8de 100644
--- a/graphics/java/android/graphics/pdf/PdfEditor.java
+++ b/graphics/java/android/graphics/pdf/PdfEditor.java
@@ -79,8 +79,12 @@
}
mInput = input;
- mNativeDocument = nativeOpen(mInput.getFd(), size);
- mPageCount = nativeGetPageCount(mNativeDocument);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ mNativeDocument = nativeOpen(mInput.getFd(), size);
+ mPageCount = nativeGetPageCount(mNativeDocument);
+ }
+
mCloseGuard.open("close");
}
@@ -102,7 +106,10 @@
public void removePage(int pageIndex) {
throwIfClosed();
throwIfPageNotInDocument(pageIndex);
- mPageCount = nativeRemovePage(mNativeDocument, pageIndex);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ mPageCount = nativeRemovePage(mNativeDocument, pageIndex);
+ }
}
/**
@@ -125,11 +132,16 @@
if (clip == null) {
Point size = new Point();
getPageSize(pageIndex, size);
- nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance,
- 0, 0, size.x, size.y);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance,
+ 0, 0, size.x, size.y);
+ }
} else {
- nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance,
- clip.left, clip.top, clip.right, clip.bottom);
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance,
+ clip.left, clip.top, clip.right, clip.bottom);
+ }
}
}
@@ -143,7 +155,10 @@
throwIfClosed();
throwIfOutSizeNull(outSize);
throwIfPageNotInDocument(pageIndex);
- nativeGetPageSize(mNativeDocument, pageIndex, outSize);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeGetPageSize(mNativeDocument, pageIndex, outSize);
+ }
}
/**
@@ -156,7 +171,10 @@
throwIfClosed();
throwIfOutMediaBoxNull(outMediaBox);
throwIfPageNotInDocument(pageIndex);
- return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox);
+ }
}
/**
@@ -169,7 +187,10 @@
throwIfClosed();
throwIfMediaBoxNull(mediaBox);
throwIfPageNotInDocument(pageIndex);
- nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox);
+ }
}
/**
@@ -182,7 +203,10 @@
throwIfClosed();
throwIfOutCropBoxNull(outCropBox);
throwIfPageNotInDocument(pageIndex);
- return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox);
+ }
}
/**
@@ -195,7 +219,10 @@
throwIfClosed();
throwIfCropBoxNull(cropBox);
throwIfPageNotInDocument(pageIndex);
- nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox);
+ }
}
/**
@@ -205,7 +232,10 @@
*/
public boolean shouldScaleForPrinting() {
throwIfClosed();
- return nativeScaleForPrinting(mNativeDocument);
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ return nativeScaleForPrinting(mNativeDocument);
+ }
}
/**
@@ -219,7 +249,10 @@
public void write(ParcelFileDescriptor output) throws IOException {
try {
throwIfClosed();
- nativeWrite(mNativeDocument, output.getFd());
+
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeWrite(mNativeDocument, output.getFd());
+ }
} finally {
IoUtils.closeQuietly(output);
}
@@ -247,7 +280,9 @@
}
private void doClose() {
- nativeClose(mNativeDocument);
+ synchronized (PdfRenderer.sPdfiumLock) {
+ nativeClose(mNativeDocument);
+ }
IoUtils.closeQuietly(mInput);
mInput = null;
mCloseGuard.close();
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index 520ebe5..cfc1309 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -99,6 +99,12 @@
* @see #close()
*/
public final class PdfRenderer implements AutoCloseable {
+ /**
+ * Any call the native pdfium code has to be single threaded as the library does not support
+ * parallel use.
+ */
+ final static Object sPdfiumLock = new Object();
+
private final CloseGuard mCloseGuard = CloseGuard.get();
private final Point mTempPoint = new Point();
@@ -154,8 +160,12 @@
}
mInput = input;
- mNativeDocument = nativeCreate(mInput.getFd(), size);
- mPageCount = nativeGetPageCount(mNativeDocument);
+
+ synchronized (sPdfiumLock) {
+ mNativeDocument = nativeCreate(mInput.getFd(), size);
+ mPageCount = nativeGetPageCount(mNativeDocument);
+ }
+
mCloseGuard.open("close");
}
@@ -189,7 +199,10 @@
*/
public boolean shouldScaleForPrinting() {
throwIfClosed();
- return nativeScaleForPrinting(mNativeDocument);
+
+ synchronized (sPdfiumLock) {
+ return nativeScaleForPrinting(mNativeDocument);
+ }
}
/**
@@ -224,7 +237,9 @@
if (mCurrentPage != null) {
mCurrentPage.close();
}
- nativeClose(mNativeDocument);
+ synchronized (sPdfiumLock) {
+ nativeClose(mNativeDocument);
+ }
try {
mInput.close();
} catch (IOException ioe) {
@@ -277,7 +292,9 @@
private Page(int index) {
Point size = mTempPoint;
- mNativePage = nativeOpenPageAndGetSize(mNativeDocument, index, size);
+ synchronized (sPdfiumLock) {
+ mNativePage = nativeOpenPageAndGetSize(mNativeDocument, index, size);
+ }
mIndex = index;
mWidth = size.x;
mHeight = size.y;
@@ -384,8 +401,10 @@
final long transformPtr = (transform != null) ? transform.native_instance : 0;
- nativeRenderPage(mNativeDocument, mNativePage, destination, contentLeft,
- contentTop, contentRight, contentBottom, transformPtr, renderMode);
+ synchronized (sPdfiumLock) {
+ nativeRenderPage(mNativeDocument, mNativePage, destination, contentLeft,
+ contentTop, contentRight, contentBottom, transformPtr, renderMode);
+ }
}
/**
@@ -412,7 +431,9 @@
}
private void doClose() {
- nativeClosePage(mNativePage);
+ synchronized (sPdfiumLock) {
+ nativeClosePage(mNativePage);
+ }
mNativePage = 0;
mCloseGuard.close();
mCurrentPage = null;
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index cce58c2..a96ca39 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -20,6 +20,7 @@
import android.annotation.WorkerThread;
import android.app.Activity;
import android.app.PendingIntent;
+import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -356,6 +357,9 @@
*
* <p> This method may block while waiting for a connection to another process, and must never
* be called from the main thread.
+ * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed
+ * at any time from the main thread, it is safer to rely on a long-lived context such as one
+ * returned from {@link Context#getApplicationContext()}.
*
* @param alias The alias of the desired private key, typically returned via
* {@link KeyChainAliasCallback#alias}.
@@ -368,7 +372,7 @@
if (alias == null) {
throw new NullPointerException("alias == null");
}
- KeyChainConnection keyChainConnection = bind(context);
+ KeyChainConnection keyChainConnection = bind(context.getApplicationContext());
try {
final IKeyChainService keyChainService = keyChainConnection.getService();
final String keyId = keyChainService.requestPrivateKey(alias);
@@ -400,6 +404,9 @@
*
* <p> This method may block while waiting for a connection to another process, and must never
* be called from the main thread.
+ * <p> As {@link Activity} and {@link Service} contexts are short-lived and can be destroyed
+ * at any time from the main thread, it is safer to rely on a long-lived context such as one
+ * returned from {@link Context#getApplicationContext()}.
*
* @param alias The alias of the desired certificate chain, typically
* returned via {@link KeyChainAliasCallback#alias}.
@@ -412,7 +419,7 @@
if (alias == null) {
throw new NullPointerException("alias == null");
}
- KeyChainConnection keyChainConnection = bind(context);
+ KeyChainConnection keyChainConnection = bind(context.getApplicationContext());
try {
IKeyChainService keyChainService = keyChainConnection.getService();
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 4848630b..56af57a 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -679,14 +679,14 @@
if (value instanceof long[]) {
long[] array = (long[]) value;
if (array.length == 1) {
- return (double) array[0];
+ return array[0];
}
throw new NumberFormatException("There are more than one component");
}
if (value instanceof int[]) {
int[] array = (int[]) value;
if (array.length == 1) {
- return (double) array[0];
+ return array[0];
}
throw new NumberFormatException("There are more than one component");
}
@@ -1083,6 +1083,7 @@
private int mThumbnailOffset;
private int mThumbnailLength;
private byte[] mThumbnailBytes;
+ private boolean mIsSupportedFile;
// Pattern to check non zero timestamp
private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -1472,9 +1473,11 @@
// Process JPEG input stream
getJpegAttributes(in);
+ mIsSupportedFile = true;
} catch (IOException e) {
// Ignore exceptions in order to keep the compatibility with the old versions of
// ExifInterface.
+ mIsSupportedFile = false;
Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
+ "(ExifInterface supports JPEG and some RAW image formats only) "
+ "or a corrupted JPEG file to ExifInterface.", e);
@@ -1553,9 +1556,9 @@
* and make a single call rather than multiple calls for each attribute.
*/
public void saveAttributes() throws IOException {
- if (mIsRaw) {
+ if (!mIsSupportedFile || mIsRaw) {
throw new UnsupportedOperationException(
- "ExifInterface does not support saving attributes on RAW formats.");
+ "ExifInterface only supports saving attributes on JPEG formats.");
}
if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
throw new UnsupportedOperationException(
@@ -2352,7 +2355,7 @@
for (int i = 0; i < EXIF_TAGS.length; ++i) {
int sum = 0;
for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
- final ExifAttribute exifAttribute = (ExifAttribute) ((Map.Entry) entry).getValue();
+ final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue();
final int size = exifAttribute.size();
if (size > 4) {
sum += size;
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index 883c8c6..b65598c 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -207,7 +207,6 @@
* object
*/
public synchronized int getResponseCode() throws IOException {
- //avoid dup validateConnection
if ((mReplyHeader.responseCode == -1)
|| (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
validateConnection();
@@ -423,8 +422,9 @@
private void validateConnection() throws IOException {
ensureOpen();
- // to sure only one privateInput object exist.
- if (mPrivateInput == null) {
+ // Make sure that a response has been recieved from remote
+ // before continuing
+ if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
startProcessing();
}
}
diff --git a/packages/CaptivePortalLogin/res/values-bn-rBD/strings.xml b/packages/CaptivePortalLogin/res/values-bn-rBD/strings.xml
index 20173b0..24cbfbd 100644
--- a/packages/CaptivePortalLogin/res/values-bn-rBD/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bn-rBD/strings.xml
@@ -4,7 +4,7 @@
<string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
<string name="action_use_network" msgid="6076184727448466030">"যেভাবে আছে সেভাবেই এই নেটওয়ার্ক ব্যবহার করুন"</string>
<string name="action_do_not_use_network" msgid="4577366536956516683">"এই নেটওয়ার্ক ব্যবহার করবেন না"</string>
- <string name="action_bar_label" msgid="917235635415966620">"নেটওয়ার্কে প্রবেশ করুন করুন"</string>
+ <string name="action_bar_label" msgid="917235635415966620">"নেটওয়ার্কে প্রবেশ করুন"</string>
<string name="ssl_error_warning" msgid="6653188881418638872">"আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"</string>
<string name="ssl_error_example" msgid="647898534624078900">"উদাহরণস্বরূপ, লগইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"</string>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index 14d4e2d..02a9127 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -53,7 +53,8 @@
*/
public static boolean isTouchType(int toolType) {
return toolType == MotionEvent.TOOL_TYPE_FINGER
- || toolType == MotionEvent.TOOL_TYPE_STYLUS;
+ || toolType == MotionEvent.TOOL_TYPE_STYLUS
+ || toolType == MotionEvent.TOOL_TYPE_UNKNOWN;
}
/**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index b82f8dd..ae8938d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -401,8 +401,13 @@
return true;
}
- // Open the Close drawer if it is closed and we're at the top of a root.
- if (size <= 1) {
+ final Intent intent = getIntent();
+ final boolean launchedExternally = intent != null && intent.getData() != null
+ && mState.action == State.ACTION_BROWSE;
+
+ // Open the Close drawer if it is closed and we're at the top of a root, but only when
+ // not launched by another app.
+ if (size <= 1 && !launchedExternally) {
mDrawer.setOpen(true);
// Remember so we don't just close it again if back is pressed again.
mDrawerLastFiddled = System.currentTimeMillis();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java b/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java
index d2e9885..b3db037 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
import android.os.UserHandle;
import android.preference.PreferenceManager;
@@ -85,6 +86,15 @@
public @interface PermissionStatus {}
/**
+ * Clears all preferences associated with a given package.
+ *
+ * <p>Typically called when a package is removed or when user asked to clear its data.
+ */
+ static void clearPackagePreferences(Context context, String packageName) {
+ clearScopedAccessPreferences(context, packageName);
+ }
+
+ /**
* Methods below are used to keep track of denied user requests on scoped directory access so
* the dialog is not offered when user checked the 'Do not ask again' box
*
@@ -108,6 +118,23 @@
getPrefs(context).edit().putInt(key, status).apply();
}
+ private static void clearScopedAccessPreferences(Context context, String packageName) {
+ final String keySubstring = "|" + packageName + "|";
+ final SharedPreferences prefs = getPrefs(context);
+ Editor editor = null;
+ for (final String key : prefs.getAll().keySet()) {
+ if (key.contains(keySubstring)) {
+ if (editor == null) {
+ editor = prefs.edit();
+ }
+ editor.remove(key);
+ }
+ }
+ if (editor != null) {
+ editor.apply();
+ }
+ }
+
private static String getScopedAccessDenialsKey(String packageName, String uuid,
String directory) {
final int userId = UserHandle.myUserId();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
index aef63af..fd1183f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
@@ -23,7 +23,7 @@
import android.net.Uri;
/**
- * Clean up {@link RecentsProvider} when packages are removed.
+ * Cleans up {@link RecentsProvider} and {@link LocalPreferences} when packages are removed.
*/
public class PackageReceiver extends BroadcastReceiver {
@Override
@@ -31,15 +31,19 @@
final ContentResolver resolver = context.getContentResolver();
final String action = intent.getAction();
+ final Uri data = intent.getData();
+ final String packageName = data == null ? null : data.getSchemeSpecificPart();
+
if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null);
-
+ if (packageName != null) {
+ LocalPreferences.clearPackagePreferences(context, packageName);
+ }
} else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
- final Uri data = intent.getData();
- if (data != null) {
- final String packageName = data.getSchemeSpecificPart();
+ if (packageName != null) {
resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE,
packageName, null);
+ LocalPreferences.clearPackagePreferences(context, packageName);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index b7c0a9c..8a6723f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -53,10 +53,13 @@
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v13.view.DragStartHelper;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnItemTouchListener;
+import android.support.v7.widget.RecyclerView.Recycler;
import android.support.v7.widget.RecyclerView.RecyclerListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.BidiFormatter;
@@ -243,7 +246,40 @@
mRecView.setAdapter(mAdapter);
- mLayout = new GridLayoutManager(getContext(), mColumnCount);
+ // Switch Access Accessibility API needs an {@link AccessibilityDelegate} to know the proper
+ // route when user selects an UI element. It usually guesses this if the element has an
+ // {@link OnClickListener}, but since we do not have one for itemView, we will need to
+ // manually route it to the right behavior. RecyclerView has its own AccessibilityDelegate,
+ // and routes it to its LayoutManager; so we must override the LayoutManager's accessibility
+ // methods to route clicks correctly.
+ mLayout = new GridLayoutManager(getContext(), mColumnCount) {
+ @Override
+ public void onInitializeAccessibilityNodeInfoForItem(
+ RecyclerView.Recycler recycler, RecyclerView.State state,
+ View host, AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
+ info.addAction(AccessibilityActionCompat.ACTION_CLICK);
+ }
+
+ @Override
+ public boolean performAccessibilityActionForItem(
+ RecyclerView.Recycler recycler, RecyclerView.State state, View view,
+ int action, Bundle args) {
+ // We are only handling click events; route all other to default implementation
+ if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
+ RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(view);
+ if (vh instanceof DocumentHolder) {
+ DocumentHolder dh = (DocumentHolder) vh;
+ if (dh.mEventListener != null) {
+ dh.mEventListener.onActivate(dh);
+ return true;
+ }
+ }
+ }
+ return super.performAccessibilityActionForItem(recycler, state, view, action,
+ args);
+ }
+ };
SpanSizeLookup lookup = mAdapter.createSpanSizeLookup();
if (lookup != null) {
mLayout.setSpanSizeLookup(lookup);
@@ -717,63 +753,57 @@
private void openDocuments(final Selection selected) {
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_OPEN);
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- // TODO: Implement support in Files activity for opening multiple docs.
- BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
- }
- }.execute(selected);
+ // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
+ List<DocumentInfo> docs = mModel.getDocuments(selected);
+ // TODO: Implement support in Files activity for opening multiple docs.
+ BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
}
private void shareDocuments(final Selection selected) {
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SHARE);
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- Intent intent;
+ // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
+ List<DocumentInfo> docs = mModel.getDocuments(selected);
+ Intent intent;
- // Filter out directories and virtual files - those can't be shared.
- List<DocumentInfo> docsForSend = new ArrayList<>();
- for (DocumentInfo doc: docs) {
- if (!doc.isDirectory() && !doc.isVirtualDocument()) {
- docsForSend.add(doc);
- }
- }
-
- if (docsForSend.size() == 1) {
- final DocumentInfo doc = docsForSend.get(0);
-
- intent = new Intent(Intent.ACTION_SEND);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setType(doc.mimeType);
- intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
-
- } else if (docsForSend.size() > 1) {
- intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
-
- final ArrayList<String> mimeTypes = new ArrayList<>();
- final ArrayList<Uri> uris = new ArrayList<>();
- for (DocumentInfo doc : docsForSend) {
- mimeTypes.add(doc.mimeType);
- uris.add(doc.derivedUri);
- }
-
- intent.setType(findCommonMimeType(mimeTypes));
- intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
-
- } else {
- return;
- }
-
- intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
- startActivity(intent);
+ // Filter out directories and virtual files - those can't be shared.
+ List<DocumentInfo> docsForSend = new ArrayList<>();
+ for (DocumentInfo doc: docs) {
+ if (!doc.isDirectory() && !doc.isVirtualDocument()) {
+ docsForSend.add(doc);
}
- }.execute(selected);
+ }
+
+ if (docsForSend.size() == 1) {
+ final DocumentInfo doc = docsForSend.get(0);
+
+ intent = new Intent(Intent.ACTION_SEND);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setType(doc.mimeType);
+ intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri);
+
+ } else if (docsForSend.size() > 1) {
+ intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+
+ final ArrayList<String> mimeTypes = new ArrayList<>();
+ final ArrayList<Uri> uris = new ArrayList<>();
+ for (DocumentInfo doc : docsForSend) {
+ mimeTypes.add(doc.mimeType);
+ uris.add(doc.derivedUri);
+ }
+
+ intent.setType(findCommonMimeType(mimeTypes));
+ intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
+
+ } else {
+ return;
+ }
+
+ intent = Intent.createChooser(intent, getActivity().getText(R.string.share_via));
+ startActivity(intent);
}
private String generateDeleteMessage(final List<DocumentInfo> docs) {
@@ -819,52 +849,51 @@
assert(!selected.isEmpty());
final DocumentInfo srcParent = getDisplayState().stack.peek();
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(final List<DocumentInfo> docs) {
- TextView message =
- (TextView) mInflater.inflate(R.layout.dialog_delete_confirmation, null);
- message.setText(generateDeleteMessage(docs));
+ // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
+ List<DocumentInfo> docs = mModel.getDocuments(selected);
- // This "insta-hides" files that are being deleted, because
- // the delete operation may be not execute immediately (it
- // may be queued up on the FileOperationService.)
- // To hide the files locally, we call the hide method on the adapter
- // ...which a live object...cannot be parceled.
- // For that reason, for now, we implement this dialog NOT
- // as a fragment (which can survive rotation and have its own state),
- // but as a simple runtime dialog. So rotating a device with an
- // active delete dialog...results in that dialog disappearing.
- // We can do better, but don't have cycles for it now.
- new AlertDialog.Builder(getActivity())
- .setView(message)
- .setPositiveButton(
- android.R.string.yes,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- // Finish selection mode first which clears selection so we
- // don't end up trying to deselect deleted documents.
- // This is done here, rather in the onActionItemClicked
- // so we can avoid de-selecting items in the case where
- // the user cancels the delete.
- if (mActionMode != null) {
- mActionMode.finish();
- } else {
- Log.w(TAG, "Action mode is null before deleting documents.");
- }
- // Hide the files in the UI...since the operation
- // might be queued up on FileOperationService.
- // We're walking a line here.
- mAdapter.hide(selected.getAll());
- FileOperations.delete(
- getActivity(), docs, srcParent, getDisplayState().stack);
- }
- })
- .setNegativeButton(android.R.string.no, null)
- .show();
- }
- }.execute(selected);
+ TextView message =
+ (TextView) mInflater.inflate(R.layout.dialog_delete_confirmation, null);
+ message.setText(generateDeleteMessage(docs));
+
+ // This "insta-hides" files that are being deleted, because
+ // the delete operation may be not execute immediately (it
+ // may be queued up on the FileOperationService.)
+ // To hide the files locally, we call the hide method on the adapter
+ // ...which a live object...cannot be parceled.
+ // For that reason, for now, we implement this dialog NOT
+ // as a fragment (which can survive rotation and have its own state),
+ // but as a simple runtime dialog. So rotating a device with an
+ // active delete dialog...results in that dialog disappearing.
+ // We can do better, but don't have cycles for it now.
+ new AlertDialog.Builder(getActivity())
+ .setView(message)
+ .setPositiveButton(
+ android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ // Finish selection mode first which clears selection so we
+ // don't end up trying to deselect deleted documents.
+ // This is done here, rather in the onActionItemClicked
+ // so we can avoid de-selecting items in the case where
+ // the user cancels the delete.
+ if (mActionMode != null) {
+ mActionMode.finish();
+ } else {
+ Log.w(TAG, "Action mode is null before deleting documents.");
+ }
+ // Hide the files in the UI...since the operation
+ // might be queued up on FileOperationService.
+ // We're walking a line here.
+ mAdapter.hide(selected.getAll());
+ FileOperations.delete(
+ getActivity(), docs, srcParent, getDisplayState().stack);
+ }
+ })
+ .setNegativeButton(android.R.string.no, null)
+ .show();
}
private void transferDocuments(final Selection selected, final @OpType int mode) {
@@ -898,25 +927,21 @@
? R.string.menu_move : R.string.menu_copy;
intent.putExtra(DocumentsContract.EXTRA_PROMPT, getResources().getString(drawerTitleId));
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- // TODO: Can this move to Fragment bundle state?
- getDisplayState().selectedDocumentsForCopy = docs;
+ // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
+ List<DocumentInfo> docs = mModel.getDocuments(selected);
+ // TODO: Can this move to Fragment bundle state?
+ getDisplayState().selectedDocumentsForCopy = docs;
- // Determine if there is a directory in the set of documents
- // to be copied? Why? Directory creation isn't supported by some roots
- // (like Downloads). This informs DocumentsActivity (the "picker")
- // to restrict available roots to just those with support.
- intent.putExtra(Shared.EXTRA_DIRECTORY_COPY, hasDirectory(docs));
- intent.putExtra(FileOperationService.EXTRA_OPERATION, mode);
+ // Determine if there is a directory in the set of documents
+ // to be copied? Why? Directory creation isn't supported by some roots
+ // (like Downloads). This informs DocumentsActivity (the "picker")
+ // to restrict available roots to just those with support.
+ intent.putExtra(Shared.EXTRA_DIRECTORY_COPY, hasDirectory(docs));
+ intent.putExtra(FileOperationService.EXTRA_OPERATION, mode);
- // This just identifies the type of request...we'll check it
- // when we reveive a response.
- startActivityForResult(intent, REQUEST_COPY_DESTINATION);
- }
-
- }.execute(selected);
+ // This just identifies the type of request...we'll check it
+ // when we reveive a response.
+ startActivityForResult(intent, REQUEST_COPY_DESTINATION);
}
private static boolean hasDirectory(List<DocumentInfo> docs) {
@@ -935,12 +960,9 @@
// Rename option is only available in menu when 1 document selected
assert(selected.size() == 1);
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- RenameDocumentFragment.show(getFragmentManager(), docs.get(0));
- }
- }.execute(selected);
+ // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
+ List<DocumentInfo> docs = mModel.getDocuments(selected);
+ RenameDocumentFragment.show(getFragmentManager(), docs.get(0));
}
@Override
@@ -1099,19 +1121,17 @@
}
}
- void copySelectionToClipboard(Selection selection) {
- assert(!selection.isEmpty());
- new GetDocumentsTask() {
- @Override
- void onDocumentsReady(List<DocumentInfo> docs) {
- mClipper.clipDocuments(docs);
- Activity activity = getActivity();
- Snackbars.makeSnackbar(activity,
- activity.getResources().getQuantityString(
- R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
- Snackbar.LENGTH_SHORT).show();
- }
- }.execute(selection);
+ void copySelectionToClipboard(Selection selected) {
+ assert(!selected.isEmpty());
+
+ // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
+ List<DocumentInfo> docs = mModel.getDocuments(selected);
+ mClipper.clipDocuments(docs);
+ Activity activity = getActivity();
+ Snackbars.makeSnackbar(activity,
+ activity.getResources().getQuantityString(
+ R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
+ Snackbar.LENGTH_SHORT).show();
}
public void pasteFromClipboard() {
@@ -1420,25 +1440,6 @@
mShadowView.draw(canvas);
}
}
- /**
- * Abstract task providing support for loading documents *off*
- * the main thread. And if it isn't obvious, creating a list
- * of documents (especially large lists) can be pretty expensive.
- */
- private abstract class GetDocumentsTask
- extends AsyncTask<Selection, Void, List<DocumentInfo>> {
- @Override
- protected final List<DocumentInfo> doInBackground(Selection... selected) {
- return mModel.getDocuments(selected[0]);
- }
-
- @Override
- protected final void onPostExecute(List<DocumentInfo> docs) {
- onDocumentsReady(docs);
- }
-
- abstract void onDocumentsReady(List<DocumentInfo> docs);
- }
@Override
public boolean isSelected(String modelId) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
index 1118171..1de3bbc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -117,7 +117,9 @@
byteCopyDocument(src, dest);
// Remove the source document.
- deleteDocument(src, srcParent);
+ if(!isCanceled()) {
+ deleteDocument(src, srcParent);
+ }
}
@Override
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 78b9927..7fe0d2f 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -566,6 +566,7 @@
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ query = query.toLowerCase();
final File parent;
synchronized (mRootsLock) {
parent = mRoots.get(rootId).path;
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index cce619e..4950af3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -873,7 +873,7 @@
}
private static int getRootFlags(int[] operationsSupported) {
- int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD;
+ int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY;
if (MtpDeviceRecord.isWritingSupported(operationsSupported)) {
rootFlag |= Root.FLAG_SUPPORTS_CREATE;
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 404047b..8c13c81 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -128,7 +128,7 @@
cursor.moveToNext();
assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
assertEquals(
- Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE,
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
getInt(cursor, Root.COLUMN_FLAGS));
assertEquals(R.drawable.ic_root_mtp, getInt(cursor, Root.COLUMN_ICON));
assertEquals("Device Storage", getString(cursor, Root.COLUMN_TITLE));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 9ed15c8..d19b460 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -210,7 +210,11 @@
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("1", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertEquals(
+ Root.FLAG_SUPPORTS_IS_CHILD |
+ Root.FLAG_SUPPORTS_CREATE |
+ Root.FLAG_LOCAL_ONLY,
+ cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device A Storage A", cursor.getString(3));
assertEquals("1", cursor.getString(4));
@@ -225,7 +229,8 @@
cursor.moveToNext();
cursor.moveToNext();
assertEquals("2", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1));
+ assertEquals(
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY, cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device B Storage B", cursor.getString(3));
assertEquals("2", cursor.getString(4));
@@ -271,7 +276,9 @@
cursor.moveToNext();
assertEquals("1", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertEquals(
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
+ cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device A", cursor.getString(3));
assertEquals("1", cursor.getString(4));
@@ -279,7 +286,9 @@
cursor.moveToNext();
assertEquals("2", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+ assertEquals(
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
+ cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device B Storage B", cursor.getString(3));
assertEquals("2", cursor.getString(4));
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 2db6fb0..31a776c 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -16,7 +16,6 @@
<com.android.printspooler.widget.PrintContentView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:printspooler="http://schemas.android.com/apk/res/com.android.printspooler"
android:id="@+id/options_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
@@ -28,12 +27,14 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingStart="8dip"
+ android:layout_marginEnd="16dp"
android:elevation="@dimen/preview_controls_elevation"
android:background="?android:attr/colorPrimary">
<Spinner
android:id="@+id/destination_spinner"
- android:layout_width="@dimen/preview_destination_spinner_width"
+ android:layout_width="wrap_content"
+ android:minWidth="@dimen/preview_destination_spinner_width"
android:layout_height="wrap_content"
android:layout_marginTop="4dip"
android:dropDownWidth="wrap_content"
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 103c157..0d8a90a 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -16,7 +16,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
style="?android:attr/spinnerItemStyle"
android:orientation="horizontal"
android:gravity="start|center_vertical">
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 3debf8e..d4e7963 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -60,7 +60,7 @@
<item quantity="one">找到 <xliff:g id="COUNT_0">%1$s</xliff:g> 台打印机</item>
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
- <string name="printer_info_desc" msgid="7181988788991581654">"关于此打印机的更多信息"</string>
+ <string name="printer_info_desc" msgid="7181988788991581654">"此打印机的详细信息"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"无法创建文件"</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"部分打印服务已停用"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜索打印机"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7d42211..682af8b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -863,4 +863,7 @@
<!-- Label for Help and feedback menu item -->
<string name="help_feedback_label">Help & feedback</string>
+ <!-- Content description for drawer menu button [CHAR_LIMIT=30]-->
+ <string name="content_description_menu_button">Menu</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index b5295da..381f903 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -34,6 +34,8 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import java.net.URISyntaxException;
import java.util.Locale;
@@ -112,6 +114,9 @@
helpMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
+ MetricsLogger.action(activity,
+ MetricsEvent.ACTION_SETTING_HELP_AND_FEEDBACK,
+ intent.getStringExtra(EXTRA_CONTEXT));
try {
activity.startActivityForResult(intent, 0);
} catch (ActivityNotFoundException exc) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 0e3e0d5..c0646ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -79,6 +79,7 @@
public static class MeasurementDetails {
public long totalSize;
public long availSize;
+ private long accountedSize;
/**
* Total apps disk usage per profiles of the current user.
@@ -126,6 +127,17 @@
* internal storage. Key is {@link UserHandle}.
*/
public SparseLongArray usersSize = new SparseLongArray();
+
+ /**
+ * Gets the total disk usage that is not accounted for.
+ *
+ * <p>
+ * Typically used by device-specific processes that the Framework has no control over.
+ */
+ public long getUnaccountedSize() {
+ final long usedSize = totalSize - availSize;
+ return usedSize - accountedSize;
+ }
}
public interface MeasurementReceiver {
@@ -219,34 +231,43 @@
}
private void addStatsLocked(PackageStats stats) {
- if (mIsPrivate) {
- long codeSize = stats.codeSize;
- long dataSize = stats.dataSize;
- long cacheSize = stats.cacheSize;
- if (Environment.isExternalStorageEmulated()) {
- // Include emulated storage when measuring internal. OBB is
- // shared on emulated storage, so treat as code.
- codeSize += stats.externalCodeSize + stats.externalObbSize;
- dataSize += stats.externalDataSize + stats.externalMediaSize;
- cacheSize += stats.externalCacheSize;
+ synchronized (mDetails) {
+ long accountedAppSize = 0;
+ long accountedCacheSize = 0;
+ if (mIsPrivate) {
+ long codeSize = stats.codeSize;
+ long dataSize = stats.dataSize;
+ long cacheSize = stats.cacheSize;
+ if (Environment.isExternalStorageEmulated()) {
+ // Include emulated storage when measuring internal. OBB is
+ // shared on emulated storage, so treat as code.
+ codeSize += stats.externalCodeSize + stats.externalObbSize;
+ dataSize += stats.externalDataSize + stats.externalMediaSize;
+ cacheSize += stats.externalCacheSize;
+ }
+ accountedAppSize += dataSize;
+ // Count code and data for current user's profiles (keys prepared in constructor)
+ if (addValueIfKeyExists(mDetails.appsSize, stats.userHandle,
+ codeSize + dataSize)) {
+ // Code is only counted once for the current user
+ accountedAppSize += codeSize;
+ }
+ // User summary only includes data (code is only counted once
+ // for the current user)
+ addValue(mDetails.usersSize, stats.userHandle, dataSize);
+
+ // Include cache for all users
+ accountedCacheSize = cacheSize;
+
+ } else {
+ // Physical storage; only count external sizes
+ addValue(mDetails.appsSize, mCurrentUser,
+ stats.externalCodeSize + stats.externalDataSize
+ + stats.externalMediaSize + stats.externalObbSize);
+ accountedCacheSize = stats.externalCacheSize;
}
-
- // Count code and data for current user's profiles (keys prepared in constructor)
- addValueIfKeyExists(mDetails.appsSize, stats.userHandle, codeSize + dataSize);
-
- // User summary only includes data (code is only counted once
- // for the current user)
- addValue(mDetails.usersSize, stats.userHandle, dataSize);
-
- // Include cache for all users
- mDetails.cacheSize += cacheSize;
-
- } else {
- // Physical storage; only count external sizes
- addValue(mDetails.appsSize, mCurrentUser,
- stats.externalCodeSize + stats.externalDataSize
- + stats.externalMediaSize + stats.externalObbSize);
- mDetails.cacheSize += stats.externalCacheSize;
+ mDetails.cacheSize += accountedCacheSize;
+ mDetails.accountedSize += accountedAppSize + accountedCacheSize;
}
}
}
@@ -375,11 +396,14 @@
for (String type : sMeasureMediaTypes) {
final File path = new File(basePath, type);
final long size = getDirectorySize(imcs, path);
+ details.accountedSize += size;
mediaMap.put(type, size);
}
// Measure misc files not counted under media
- addValue(details.miscSize, userId, measureMisc(imcs, basePath));
+ final long miscSize = measureMisc(imcs, basePath);
+ details.accountedSize += miscSize;
+ addValue(details.miscSize, userId, miscSize);
}
if (mSharedVolume.getType() == VolumeInfo.TYPE_EMULATED) {
@@ -388,6 +412,9 @@
for (UserInfo user : users) {
final File userPath = mSharedVolume.getPathForUser(user.id);
final long size = getDirectorySize(imcs, userPath);
+ if (user.id != UserHandle.USER_SYSTEM) {
+ details.accountedSize += size;
+ }
addValue(details.usersSize, user.id, size);
}
}
@@ -468,10 +495,12 @@
array.put(key, array.get(key) + value);
}
- private static void addValueIfKeyExists(SparseLongArray array, int key, long value) {
+ private static boolean addValueIfKeyExists(SparseLongArray array, int key, long value) {
final int index = array.indexOfKey(key);
if (index >= 0) {
array.put(key, array.valueAt(index) + value);
+ return true;
}
+ return false;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 6658c14..a50b366 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -226,6 +226,7 @@
public void showMenuIcon() {
mShowingMenu = true;
getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
+ getActionBar().setHomeActionContentDescription(R.string.content_description_menu_button);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 284827b..aae9cf6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -29,7 +29,6 @@
import android.util.AttributeSet;
import android.util.SparseArray;
import android.widget.TextView;
-
import com.android.settingslib.R;
public class AccessPointPreference extends Preference {
@@ -44,13 +43,14 @@
private final StateListDrawable mWifiSld;
private final int mBadgePadding;
private final UserBadgeCache mBadgeCache;
-
private TextView mTitleView;
+
private boolean mForSavedNetworks = false;
private AccessPoint mAccessPoint;
private Drawable mBadge;
private int mLevel;
private CharSequence mContentDescription;
+ private int mDefaultIconResId;
static final int[] WIFI_CONNECTION_STRENGTH = {
R.string.accessibility_wifi_one_bar,
@@ -85,6 +85,24 @@
refresh();
}
+ public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
+ int iconResId, boolean forSavedNetworks) {
+ super(context);
+ mBadgeCache = cache;
+ mAccessPoint = accessPoint;
+ mForSavedNetworks = forSavedNetworks;
+ mAccessPoint.setTag(this);
+ mLevel = -1;
+ mDefaultIconResId = iconResId;
+
+ mWifiSld = (StateListDrawable) context.getTheme()
+ .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
+
+ // Distance from the end of the title at which this AP's user badge should sit.
+ mBadgePadding = context.getResources()
+ .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+ }
+
public AccessPoint getAccessPoint() {
return mAccessPoint;
}
@@ -112,7 +130,7 @@
protected void updateIcon(int level, Context context) {
if (level == -1) {
- setIcon(null);
+ safeSetDefaultIcon();
} else {
if (getIcon() == null) {
// To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
@@ -124,16 +142,24 @@
? STATE_SECURED
: STATE_NONE);
Drawable drawable = mWifiSld.getCurrent();
- if (!mForSavedNetworks) {
+ if (!mForSavedNetworks && drawable != null) {
setIcon(drawable);
- } else {
- setIcon(null);
+ return;
}
}
+ safeSetDefaultIcon();
}
}
}
+ private void safeSetDefaultIcon() {
+ if (mDefaultIconResId != 0) {
+ setIcon(mDefaultIconResId);
+ } else {
+ setIcon(null);
+ }
+ }
+
protected void updateBadge(Context context) {
WifiConfiguration config = mAccessPoint.getConfig();
if (config != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 0f7fe6f..a43c398 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -114,7 +114,7 @@
public class SettingsProvider extends ContentProvider {
private static final boolean DEBUG = false;
- private static final boolean DROP_DATABASE_ON_MIGRATION = !Build.IS_DEBUGGABLE;
+ private static final boolean DROP_DATABASE_ON_MIGRATION = true;
private static final String LOG_TAG = "SettingsProvider";
@@ -2142,6 +2142,12 @@
// Now upgrade should work fine.
onUpgradeLocked(mUserId, oldVersion, newVersion);
+
+ // Make a note what happened, so we don't wonder why data was lost
+ String reason = "Settings rebuilt! Current version: "
+ + curVersion + " while expected: " + newVersion;
+ getGlobalSettingsLocked().insertSettingLocked(
+ Settings.Global.DATABASE_DOWNGRADE_REASON, reason, "android");
}
// Set the global settings version if owner.
@@ -2411,7 +2417,7 @@
}
if (currentVersion != newVersion) {
- Slog.w("SettingsProvider", "warning: upgrading settings database to version "
+ Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
+ newVersion + " left it at "
+ currentVersion + " instead; this is probably a bug", new Throwable());
if (DEBUG) {
diff --git a/packages/Shell/Android.mk b/packages/Shell/Android.mk
index 73a0449..81ab2ff 100644
--- a/packages/Shell/Android.mk
+++ b/packages/Shell/Android.mk
@@ -12,6 +12,8 @@
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.shell.*
+
include $(BUILD_PACKAGE)
include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
index 8b07599..9fd80d3 100644
--- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -132,6 +132,7 @@
if (!getFileForDocId(documentId).delete()) {
throw new FileNotFoundException("Failed to delete: " + documentId);
}
+ getContext().getContentResolver().notifyChange(getNotificationUri(), null);
}
// This is used by BugreportProgressService so that the notification uri shared by
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index bdb103a..de25115 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -150,9 +150,6 @@
<!-- DevicePolicyManager get user restrictions -->
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
- <!-- Needed for passing extras with intent ACTION_SHOW_ADMIN_SUPPORT_DETAILS -->
- <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
-
<!-- TV picture-in-picture -->
<uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" />
diff --git a/packages/SystemUI/res/values-bs-rBA-land/strings.xml b/packages/SystemUI/res/values-bs-rBA-land/strings.xml
index bdc652a..56a4ad2 100644
--- a/packages/SystemUI/res/values-bs-rBA-land/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA-land/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="toast_rotation_locked" msgid="7609673011431556092">"Ekran je sada zaključan u pejzažnom prikazu."</string>
+ <string name="toast_rotation_locked" msgid="7609673011431556092">"Ekran je sada zaključan u vodoravnom prikazu."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index d0d6c50..2aad63c 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -32,7 +32,7 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ծանուցումներ չկան"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string>
- <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցը լիցքաթափվում է"</string>
+ <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցի լիցքը սպառվում է"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>: Մարտկոցի տնտեսումը միացված է:"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB լիցքավորումը չի աջակցվում:\nՕգտվեք միայն գործող լիցքավորիչից:"</string>
diff --git a/packages/SystemUI/res/values-it/strings_car.xml b/packages/SystemUI/res/values-it/strings_car.xml
index ae26c9e..19c4e2b 100644
--- a/packages/SystemUI/res/values-it/strings_car.xml
+++ b/packages/SystemUI/res/values-it/strings_car.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="car_lockscreen_disclaimer_title" msgid="7997539137376896441">"Guida in modo sicuro"</string>
- <string name="car_lockscreen_disclaimer_text" msgid="3061224684092952864">"È necessario essere sempre pienamente coscienti delle condizioni di guida e rispettare le leggi vigenti. Le indicazioni stradali potrebbero essere imprecise, incomplete, pericolose, non adatte, vietate o implicare l\'attraversamento di confini. Anche le informazioni sulle attività commerciali potrebbero essere imprecise o incomplete. I dati non vengono forniti in tempo reale e non è possibile garantire la precisione della geolocalizzazione. Non maneggiare il dispositivo mobile e non utilizzare app non progettate per Android Auto durante la guida."</string>
+ <string name="car_lockscreen_disclaimer_text" msgid="3061224684092952864">"È necessario essere sempre pienamente informati sulle condizioni della strada e rispettare la legislazione vigente. Le indicazioni stradali potrebbero essere imprecise, incomplete, pericolose, inadatte, vietate o richiedere l\'attraversamento di aree amministrative. Anche le informazioni sugli esercizi commerciali potrebbero essere imprecise o incomplete. I dati forniti non sono aggiornati in tempo reale e non è possibile garantire la precisione della geolocalizzazione. Non maneggiare dispositivi mobili e app non destinate ad Android Auto durante la guida."</string>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6c48b25..a670df5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1243,11 +1243,6 @@
<!-- Option to use new paging layout in quick settings [CHAR LIMIT=60] -->
<string name="qs_paging" translatable="false">Use the new Quick Settings</string>
- <!-- Toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=60]-->
- <string name="overview_nav_bar_gesture">Enable split-screen swipe-up gesture</string>
- <!-- Description for the toggle to enable the gesture to enter split-screen by swiping up from the Overview button. [CHAR LIMIT=NONE]-->
- <string name="overview_nav_bar_gesture_desc">Enable gesture to enter split-screen by swiping up from the Overview button</string>
-
<!-- Category in the System UI Tuner settings, where new/experimental
settings are -->
<string name="experimental">Experimental</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1ee13e9..1e3b841 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -42,7 +42,7 @@
</style>
<!-- Theme used for the activity that shows when the system forced an app to be resizable -->
- <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
+ <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowBackground">@drawable/forced_resizable_background</item>
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
diff --git a/packages/SystemUI/res/xml/other_settings.xml b/packages/SystemUI/res/xml/other_settings.xml
index 3c872fa..ce636cd 100644
--- a/packages/SystemUI/res/xml/other_settings.xml
+++ b/packages/SystemUI/res/xml/other_settings.xml
@@ -18,11 +18,6 @@
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:title="@string/other">
- <com.android.systemui.tuner.TunerSwitch
- android:key="overview_nav_bar_gesture"
- android:title="@string/overview_nav_bar_gesture"
- android:summary="@string/overview_nav_bar_gesture_desc" />
-
<!-- importance -->
<Preference
android:key="power_notification_controls"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 84901ee..b393cf7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -90,10 +90,10 @@
}
@Override // Binder interface
- public void setOccluded(boolean isOccluded) {
+ public void setOccluded(boolean isOccluded, boolean animate) {
Trace.beginSection("KeyguardService.mBinder#setOccluded");
checkPermission();
- mKeyguardViewMediator.setOccluded(isOccluded);
+ mKeyguardViewMediator.setOccluded(isOccluded, animate);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index de0c77b..4449435 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1114,11 +1114,11 @@
/**
* Notify us when the keyguard is occluded by another window
*/
- public void setOccluded(boolean isOccluded) {
+ public void setOccluded(boolean isOccluded, boolean animate) {
Trace.beginSection("KeyguardViewMediator#setOccluded");
if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded);
mHandler.removeMessages(SET_OCCLUDED);
- Message msg = mHandler.obtainMessage(SET_OCCLUDED, (isOccluded ? 1 : 0), 0);
+ Message msg = mHandler.obtainMessage(SET_OCCLUDED, isOccluded ? 1 : 0, animate ? 1 : 0);
mHandler.sendMessage(msg);
Trace.endSection();
}
@@ -1126,7 +1126,7 @@
/**
* Handles SET_OCCLUDED message sent by setOccluded()
*/
- private void handleSetOccluded(boolean isOccluded) {
+ private void handleSetOccluded(boolean isOccluded, boolean animate) {
Trace.beginSection("KeyguardViewMediator#handleSetOccluded");
synchronized (KeyguardViewMediator.this) {
if (mHiding && isOccluded) {
@@ -1137,7 +1137,7 @@
if (mOccluded != isOccluded) {
mOccluded = isOccluded;
- mStatusBarKeyguardViewManager.setOccluded(isOccluded);
+ mStatusBarKeyguardViewManager.setOccluded(isOccluded, animate);
updateActivityLockScreenState();
adjustStatusBarLocked();
}
@@ -1468,7 +1468,7 @@
break;
case SET_OCCLUDED:
Trace.beginSection("KeyguardViewMediator#handleMessage SET_OCCLUDED");
- handleSetOccluded(msg.arg1 != 0);
+ handleSetOccluded(msg.arg1 != 0, msg.arg2 != 0);
Trace.endSection();
break;
case KEYGUARD_TIMEOUT:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
index 9403664..9214eef 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -35,4 +35,5 @@
in Rect initialBounds);
void onDraggingInRecents(float distanceFromTop);
void onDraggingInRecentsEnded(float velocity);
+ void showCurrentUserToast(int msgResId, int msgLength);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index e117bfe..7207463 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -18,10 +18,12 @@
import android.app.ActivityManager;
import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -53,6 +55,7 @@
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
@@ -96,7 +99,7 @@
// and does not reside in the home stack.
private String mOverrideRecentsPackageName;
- private Handler mHandler;
+ private Handler mHandler = new Handler();
private RecentsImpl mImpl;
private int mDraggingInRecentsCurrentUser;
@@ -162,6 +165,20 @@
}
};
+
+ private BroadcastReceiver mSystemUserUnlockedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId != UserHandle.USER_NULL) {
+ mImpl.onUserUnlocked(userId);
+ }
+ }
+ }
+ };
+
+
/**
* Returns the callbacks interface that non-system users can call.
*/
@@ -191,7 +208,7 @@
sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
- mHandler = new Handler();
+
UiModeManager uiModeManager = (UiModeManager) mContext.
getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
@@ -221,6 +238,12 @@
// For the system user, initialize an instance of the interface that we can pass to the
// secondary user
mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
+
+ // Listen for user-unlocked to kick off preloading recents
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ mContext.registerReceiverAsUser(mSystemUserUnlockedReceiver, UserHandle.SYSTEM, filter,
+ null /* permission */, null /* scheduler */);
} else {
// For the secondary user, bind to the primary user's service to get a persistent
// interface to register its implementation and to later update its state
@@ -455,8 +478,8 @@
mDraggingInRecentsCurrentUser = currentUser;
return true;
} else {
- Toast.makeText(mContext, R.string.recents_incompatible_app_message,
- Toast.LENGTH_SHORT).show();
+ EventBus.getDefault().send(new ShowUserToastEvent(
+ R.string.recents_incompatible_app_message, Toast.LENGTH_SHORT));
return false;
}
} else {
@@ -674,6 +697,27 @@
mImpl.onConfigurationChanged();
}
+ public final void onBusEvent(ShowUserToastEvent event) {
+ int currentUser = sSystemServicesProxy.getCurrentUser();
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength);
+ } else {
+ if (mSystemToUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.showCurrentUserToast(event.msgResId, event.msgLength);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
+ }
+ }
+
/**
* Attempts to register with the system user.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 2757fc4..a7f2716 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -40,6 +40,7 @@
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.R;
@@ -186,7 +187,7 @@
reloadResources();
}
- public void onBootCompleted() {
+ public void onUserUnlocked(int userId) {
// When we start, preload the data associated with the previous recent tasks.
// We can use a new plan since the caches will be the same.
RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -199,6 +200,10 @@
loader.loadTasks(mContext, plan, launchOpts);
}
+ public void onBootCompleted() {
+ // Do nothing
+ }
+
public void onConfigurationChanged() {
reloadResources();
mDummyStackView.reloadOnConfigurationChange();
@@ -392,6 +397,10 @@
EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
}
+ public void onShowCurrentUserToast(int msgResId, int msgLength) {
+ Toast.makeText(mContext, msgResId, msgLength).show();
+ }
+
/**
* Transitions to the next recent task in the stack.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
index 60bf760..ff9e89e9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
@@ -38,6 +38,7 @@
private static final int MSG_DOCK_TOP_TASK = 7;
private static final int MSG_ON_DRAGGING_IN_RECENTS = 8;
private static final int MSG_ON_DRAGGING_IN_RECENTS_ENDED = 9;
+ private static final int MSG_SHOW_USER_TOAST = 10;
private RecentsImpl mImpl;
@@ -109,6 +110,11 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DRAGGING_IN_RECENTS_ENDED, velocity));
}
+ @Override
+ public void showCurrentUserToast(int msgResId, int msgLength) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_USER_TOAST, msgResId, msgLength));
+ }
+
private final Handler mHandler = new Handler() {
@Override
@@ -147,6 +153,9 @@
case MSG_ON_DRAGGING_IN_RECENTS_ENDED:
mImpl.onDraggingInRecentsEnded((Float) msg.obj);
break;
+ case MSG_SHOW_USER_TOAST:
+ mImpl.onShowCurrentUserToast(msg.arg1, msg.arg2);
+ break;
default:
super.handleMessage(msg);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
new file mode 100644
index 0000000..e2b39c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.android.systemui.recents.events.component;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when we want to show a toast for the current user.
+ */
+public class ShowUserToastEvent extends EventBus.Event {
+
+ public final int msgResId;
+ public final int msgLength;
+
+ public ShowUserToastEvent(int msgResId, int msgLength) {
+ this.msgResId = msgResId;
+ this.msgLength = msgLength;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 702b076d..fce7f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -1258,7 +1258,7 @@
String innerPrefix = prefix + " ";
writer.print(prefix); writer.print(TAG);
- writer.write(" numStackTasks="); writer.write(mNumStackTasks);
+ writer.write(" numStackTasks="); writer.print(mNumStackTasks);
writer.println();
writer.print(innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index 5f083d5..5920f46 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -20,12 +20,14 @@
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.widget.Toast;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
+import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.stackdivider.events.StartedDragingEvent;
@@ -100,9 +102,8 @@
}
private void activityDismissingDockedStack() {
- Toast toast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
- Toast.LENGTH_SHORT);
- toast.show();
+ EventBus.getDefault().send(new ShowUserToastEvent(
+ R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
}
private void showPending() {
@@ -112,7 +113,7 @@
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchTaskId(mPendingTaskIds.valueAt(i));
options.setTaskOverlay(true);
- mContext.startActivity(intent, options.toBundle());
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
}
mPendingTaskIds.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 02fdd3f..68de16b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -507,7 +507,7 @@
int intrinsicHeight = getIntrinsicHeight();
mIsPinned = pinned;
if (intrinsicHeight != getIntrinsicHeight()) {
- notifyHeightChanged(false);
+ notifyHeightChanged(false /* needsAnimation */);
}
if (pinned) {
setIconAnimationRunning(true);
@@ -840,8 +840,6 @@
}
public void resetHeight() {
- mMaxExpandHeight = 0;
- mHeadsUpHeight = 0;
onHeightReset();
requestLayout();
}
@@ -1290,7 +1288,7 @@
}
mHeadsUpHeight = headsUpChild.getHeight();
if (intrinsicBefore != getIntrinsicHeight()) {
- notifyHeightChanged(false /* needsAnimation */);
+ notifyHeightChanged(true /* needsAnimation */);
}
}
@@ -1398,7 +1396,7 @@
if (isChildInGroup()) {
mGroupManager.setGroupExpanded(mStatusBarNotification, true);
}
- notifyHeightChanged(false);
+ notifyHeightChanged(false /* needsAnimation */);
}
public void setChildrenExpanded(boolean expanded, boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
index f98b9e5..df4566b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightStatusBarController.java
@@ -27,7 +27,7 @@
/**
* Controls how light status bar flag applies to the icons.
*/
-public class LightStatusBarController {
+public class LightStatusBarController implements BatteryController.BatteryStateChangeCallback {
private final StatusBarIconController mIconController;
private final BatteryController mBatteryController;
@@ -37,6 +37,7 @@
private int mDockedStackVisibility;
private boolean mFullscreenLight;
private boolean mDockedLight;
+ private int mLastStatusBarMode;
private final Rect mLastFullscreenBounds = new Rect();
private final Rect mLastDockedBounds = new Rect();
@@ -45,6 +46,7 @@
BatteryController batteryController) {
mIconController = iconController;
mBatteryController = batteryController;
+ batteryController.addStateChangedCallback(this);
}
public void setFingerprintUnlockController(
@@ -73,6 +75,7 @@
}
mFullscreenStackVisibility = newFullscreen;
mDockedStackVisibility = newDocked;
+ mLastStatusBarMode = statusBarMode;
mLastFullscreenBounds.set(fullscreenStackBounds);
mLastDockedBounds.set(dockedStackBounds);
}
@@ -123,4 +126,16 @@
mIconController.setIconsDark(true, animateChange());
}
}
+
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ onSystemUiVisibilityChanged(mFullscreenStackVisibility, mDockedStackVisibility,
+ 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, true /* sbModeChange*/,
+ mLastStatusBarMode);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index af85101..a6a5742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -92,6 +92,7 @@
* Whether an instant expand request is currently pending and we are just waiting for layout.
*/
private boolean mInstantExpanding;
+ private boolean mAnimateAfterExpanding;
PanelBar mBar;
@@ -656,7 +657,7 @@
vel = 0;
}
mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
- if (expandBecauseOfFalsing) {
+ if (vel == 0) {
animator.setDuration(350);
}
} else {
@@ -870,6 +871,7 @@
}
mInstantExpanding = true;
+ mAnimateAfterExpanding = animate;
mUpdateFlingOnLayout = false;
abortAnimations();
cancelPeek();
@@ -894,7 +896,7 @@
if (mStatusBar.getStatusBarWindow().getHeight()
!= mStatusBar.getStatusBarHeight()) {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
- if (animate) {
+ if (mAnimateAfterExpanding) {
notifyExpandingStarted();
fling(0, true /* expand */);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 96fb7a8..b1bea02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -279,6 +279,14 @@
*/
private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
+ /**
+ * Never let the alpha become zero for surfaces that draw with SRC - otherwise the RenderNode
+ * won't draw anything and uninitialized memory will show through
+ * if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
+ * libhwui.
+ */
+ private static final float SRC_MIN_ALPHA = 0.002f;
+
static {
boolean onlyCoreApps;
boolean freeformWindowManagement;
@@ -2209,17 +2217,13 @@
if (mBackdrop.getVisibility() != View.VISIBLE) {
mBackdrop.setVisibility(View.VISIBLE);
if (allowEnterAnimation) {
- mBackdrop.animate().alpha(1f).withEndAction(new Runnable() {
- @Override
- public void run() {
- mStatusBarWindowManager.setBackdropShowing(true);
- }
- });
+ mBackdrop.setAlpha(SRC_MIN_ALPHA);
+ mBackdrop.animate().alpha(1f);
} else {
mBackdrop.animate().cancel();
mBackdrop.setAlpha(1f);
- mStatusBarWindowManager.setBackdropShowing(true);
}
+ mStatusBarWindowManager.setBackdropShowing(true);
metaDataChanged = true;
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
@@ -2282,11 +2286,7 @@
} else {
mStatusBarWindowManager.setBackdropShowing(false);
mBackdrop.animate()
- // Never let the alpha become zero - otherwise the RenderNode
- // won't draw anything and uninitialized memory will show through
- // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
- // libhwui.
- .alpha(0.002f)
+ .alpha(SRC_MIN_ALPHA)
.setInterpolator(Interpolators.ACCELERATE_DECELERATE)
.setDuration(300)
.setStartDelay(0)
@@ -2301,7 +2301,6 @@
});
if (mKeyguardFadingAway) {
mBackdrop.animate()
-
// Make it disappear faster, as the focus should be on the activity
// behind.
.setDuration(mKeyguardFadingAwayDuration / 2)
@@ -4120,6 +4119,15 @@
}
/**
+ * Plays the animation when an activity that was occluding Keyguard goes away.
+ */
+ public void animateKeyguardUnoccluding() {
+ mScrimController.animateKeyguardUnoccluding(500);
+ mNotificationPanel.setExpandedFraction(0f);
+ animateExpandNotificationsPanel();
+ }
+
+ /**
* Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
* Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
* because the launched app crashed or something else went wrong.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 8b87a7f..73a95c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -196,6 +196,14 @@
}
}
+ public void animateKeyguardUnoccluding(long duration) {
+ mAnimateChange = false;
+ setScrimBehindColor(0f);
+ mAnimateChange = true;
+ scheduleUpdate();
+ mDurationOverride = duration;
+ }
+
public void animateGoingToFullShade(long delay, long duration) {
mDurationOverride = duration;
mAnimationDelay = delay;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c72f994..def4bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -242,7 +242,7 @@
return mStatusBarWindowManager.isShowingWallpaper();
}
- public void setOccluded(boolean occluded) {
+ public void setOccluded(boolean occluded, boolean animate) {
if (occluded && !mOccluded && mShowing) {
if (mPhoneStatusBar.isInLaunchTransition()) {
mOccluded = true;
@@ -258,9 +258,12 @@
}
}
mOccluded = occluded;
- mPhoneStatusBar.updateMediaMetaData(false, false);
+ mPhoneStatusBar.updateMediaMetaData(false, animate && !occluded);
mStatusBarWindowManager.setKeyguardOccluded(occluded);
reset();
+ if (animate && !occluded) {
+ mPhoneStatusBar.animateKeyguardUnoccluding();
+ }
}
public boolean isOccluded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 61bac2d..e6066aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -59,6 +59,7 @@
private AudioManager mAudioManager;
private boolean mGestureAborted;
private boolean mLongClicked;
+ private OnClickListener mOnClickListener;
private final Runnable mCheckLongPress = new Runnable() {
public void run() {
@@ -109,6 +110,12 @@
mCode = code;
}
+ @Override
+ public void setOnClickListener(OnClickListener onClickListener) {
+ super.setOnClickListener(onClickListener);
+ mOnClickListener = onClickListener;
+ }
+
public void loadAsync(String uri) {
new AsyncTask<String, Void, Drawable>() {
@Override
@@ -190,6 +197,7 @@
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
+ playSoundEffect(SoundEffectConstants.CLICK);
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
break;
@@ -215,14 +223,14 @@
if (doIt) {
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- playSoundEffect(SoundEffectConstants.CLICK);
} else {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
} else {
// no key code, just a regular ImageView
- if (doIt) {
- performClick();
+ if (doIt && mOnClickListener != null) {
+ mOnClickListener.onClick(this);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
}
removeCallbacks(mCheckLongPress);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c8c4310..d1de38c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -216,7 +216,6 @@
private float mTopPaddingOverflow;
private boolean mDontReportNextOverScroll;
private boolean mDontClampNextScroll;
- private boolean mRequestViewResizeAnimationOnLayout;
private boolean mNeedViewResizeAnimation;
private View mExpandedGroupView;
private boolean mEverythingNeedsAnimation;
@@ -518,10 +517,6 @@
setMaxLayoutHeight(getHeight());
updateContentHeight();
clampScrollPosition();
- if (mRequestViewResizeAnimationOnLayout) {
- requestAnimationOnViewResize(null);
- mRequestViewResizeAnimationOnLayout = false;
- }
requestChildrenUpdate();
updateFirstAndLastBackgroundViews();
}
@@ -1966,9 +1961,9 @@
}
private void applyCurrentBackgroundBounds() {
- if (!mFadingOut) {
- mScrimController.setExcludedBackgroundArea(mCurrentBounds);
- }
+ mScrimController.setExcludedBackgroundArea(
+ mFadingOut || mParentFadingOut || mAmbientState.isDark() ? null
+ : mCurrentBounds);
invalidate();
}
@@ -3104,9 +3099,6 @@
@Override
public void onReset(ExpandableView view) {
- if (mIsExpanded && mAnimationsEnabled) {
- mRequestViewResizeAnimationOnLayout = true;
- }
updateAnimationState(view);
updateChronometerForChild(view);
}
@@ -3847,11 +3839,7 @@
}
private void updateFadingState() {
- if (mFadingOut || mParentFadingOut || mAmbientState.isDark()) {
- mScrimController.setExcludedBackgroundArea(null);
- } else {
- applyCurrentBackgroundBounds();
- }
+ applyCurrentBackgroundBounds();
updateSrcDrawing();
}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index c9c5805..d122ccc 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -58,6 +58,42 @@
# UI it doesn't own. This is necessary to allow screenshots to be taken
LOCAL_CERTIFICATE := platform
+# Provide jack a list of classes to exclude from code coverage.
+# This is needed because the SystemUITests compile SystemUI source directly, rather than using
+# LOCAL_INSTRUMENTATION_FOR := SystemUI.
+#
+# We want to exclude the test classes from code coverage measurements, but they share the same
+# package as the rest of SystemUI so they can't be easily filtered by package name.
+#
+# Generate a comma separated list of patterns based on the test source files under src/
+# SystemUI classes are in ../src/ so they won't be excluded.
+# Example:
+# Input files: src/com/android/systemui/Test.java src/com/android/systemui/AnotherTest.java
+# Generated exclude list: com.android.systemui.Test*,com.android.systemui.AnotherTest*
+
+# Filter all src files under src/ to just java files
+local_java_files := $(filter %.java,$(call all-java-files-under, src))
+# Transform java file names into full class names.
+# This only works if the class name matches the file name and the directory structure
+# matches the package.
+local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
+local_comma := ,
+local_empty :=
+local_space := $(local_empty) $(local_empty)
+# Convert class name list to jacoco exclude list
+# This appends a * to all classes and replace the space separators with commas.
+jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
+
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.systemui.*
+LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := com.android.systemui.tests.*,$(jacoco_exclude)
+
include frameworks/base/packages/SettingsLib/common.mk
include $(BUILD_PACKAGE)
+
+# Reset variables
+local_java_files :=
+local_classes :=
+local_comma :=
+local_space :=
+jacoco_exclude :=
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 4865e96..e2e1e44 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -25,5 +25,5 @@
<string name="duration" msgid="3584782459928719435">"Durată:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"Trimise:"</string>
<string name="data_received" msgid="4062776929376067820">"Primite:"</string>
- <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> (de) octeți/<xliff:g id="NUMBER_1">%2$s</xliff:g> (de) pachete"</string>
+ <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> octeți/<xliff:g id="NUMBER_1">%2$s</xliff:g> pachete"</string>
</resources>
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 5099db7..8a0dfe5 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2207,7 +2207,12 @@
// CATEGORY: SETTINGS
ACTION_AMBIENT_DISPLAY = 495;
+ // ACTION: Settings -> [sub settings activity] -> Options menu -> Help & Support
+ // SUBTYPE: sub settings classname
+ ACTION_SETTING_HELP_AND_FEEDBACK = 496;
+
// ---- End N-MR1 Constants, all N-MR1 constants go above this line ----
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 695ea60..8dca14f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2208,6 +2208,8 @@
AccessibilityServiceInfo mAccessibilityServiceInfo;
+ // The service that's bound to this instance. Whenever this value is non-null, this
+ // object is registered as a death recipient
IBinder mService;
IAccessibilityServiceClient mServiceInterface;
@@ -2342,14 +2344,14 @@
}
} else {
userState.mBindingServices.add(mComponentName);
- mService = userState.mUiAutomationServiceClient.asBinder();
mMainHandler.post(new Runnable() {
@Override
public void run() {
// Simulate asynchronous connection since in onServiceConnected
// we may modify the state data in case of an error but bind is
// called while iterating over the data and bad things can happen.
- onServiceConnected(mComponentName, mService);
+ onServiceConnected(mComponentName,
+ userState.mUiAutomationServiceClient.asBinder());
}
});
userState.mUiAutomationService = this;
@@ -2441,7 +2443,19 @@
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
synchronized (mLock) {
- mService = service;
+ if (mService != service) {
+ if (mService != null) {
+ mService.unlinkToDeath(this, 0);
+ }
+ mService = service;
+ try {
+ mService.linkToDeath(this, 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed registering death link");
+ binderDied();
+ return;
+ }
+ }
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
UserState userState = getUserStateLocked(mUserId);
addServiceLocked(this, userState);
@@ -3075,7 +3089,6 @@
}
public void onAdded() throws RemoteException {
- linkToOwnDeathLocked();
final long identity = Binder.clearCallingIdentity();
try {
mWindowManagerService.addWindowToken(mOverlayWindowToken,
@@ -3092,17 +3105,6 @@
} finally {
Binder.restoreCallingIdentity(identity);
}
- unlinkToOwnDeathLocked();
- }
-
- public void linkToOwnDeathLocked() throws RemoteException {
- mService.linkToDeath(this, 0);
- }
-
- public void unlinkToOwnDeathLocked() {
- if (mService != null) {
- mService.unlinkToDeath(this, 0);
- }
}
public void resetLocked() {
@@ -3115,7 +3117,10 @@
} catch (RemoteException re) {
/* ignore */
}
- mService = null;
+ if (mService != null) {
+ mService.unlinkToDeath(this, 0);
+ mService = null;
+ }
mServiceInterface = null;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 9d3889b..b5fcb5c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2266,6 +2266,7 @@
pw.print(info.updatePeriodMillis);
pw.print(" resizeMode=");
pw.print(info.resizeMode);
+ pw.print(" widgetCategory=");
pw.print(info.widgetCategory);
pw.print(" autoAdvanceViewId=");
pw.print(info.autoAdvanceViewId);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index b5b0cd8..4caeba8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -875,7 +875,7 @@
return AppOpsManager.MODE_IGNORED;
}
synchronized (this) {
- if (isOpRestricted(uid, code, resolvedPackageName)) {
+ if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -1024,7 +1024,7 @@
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
- if (isOpRestricted(uid, code, packageName)) {
+ if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
if (op.duration == -1) {
@@ -1082,7 +1082,7 @@
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
- if (isOpRestricted(uid, code, resolvedPackageName)) {
+ if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1308,7 +1308,7 @@
return op;
}
- private boolean isOpRestricted(int uid, int code, String packageName) {
+ private boolean isOpRestrictedLocked(int uid, int code, String packageName) {
int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size();
@@ -2210,25 +2210,33 @@
private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
int userHandle, String[] exceptionPackages) {
- ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
+ boolean notifyChange = false;
- if (restrictionState == null) {
- try {
- restrictionState = new ClientRestrictionState(token);
- } catch (RemoteException e) {
- return;
+ synchronized (AppOpsService.this) {
+ ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
+
+ if (restrictionState == null) {
+ try {
+ restrictionState = new ClientRestrictionState(token);
+ } catch (RemoteException e) {
+ return;
+ }
+ mOpUserRestrictions.put(token, restrictionState);
}
- mOpUserRestrictions.put(token, restrictionState);
+
+ if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
+ notifyChange = true;
+ }
+
+ if (restrictionState.isDefault()) {
+ mOpUserRestrictions.remove(token);
+ restrictionState.destroy();
+ }
}
- if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
+ if (notifyChange) {
notifyWatchersOfChange(code);
}
-
- if (restrictionState.isDefault()) {
- mOpUserRestrictions.remove(token);
- restrictionState.destroy();
- }
}
private void notifyWatchersOfChange(int code) {
@@ -2263,10 +2271,12 @@
@Override
public void removeUser(int userHandle) throws RemoteException {
checkSystemUid("removeUser");
- final int tokenCount = mOpUserRestrictions.size();
- for (int i = tokenCount - 1; i >= 0; i--) {
- ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
- opRestrictions.removeUser(userHandle);
+ synchronized (AppOpsService.this) {
+ final int tokenCount = mOpUserRestrictions.size();
+ for (int i = tokenCount - 1; i >= 0; i--) {
+ ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
+ opRestrictions.removeUser(userHandle);
+ }
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index c9bbb76..2f96b20 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -22,6 +22,7 @@
import android.accounts.AccountAndUser;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerInternal;
import android.accounts.AuthenticatorDescription;
import android.accounts.CantAddAccountActivity;
import android.accounts.GrantCredentialsPermissionActivity;
@@ -29,11 +30,14 @@
import android.accounts.IAccountAuthenticatorResponse;
import android.accounts.IAccountManager;
import android.accounts.IAccountManagerResponse;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -46,9 +50,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -72,11 +78,14 @@
import android.os.Message;
import android.os.Parcel;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -86,6 +95,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -237,6 +247,13 @@
+ " AND " + ACCOUNTS_NAME + "=?"
+ " AND " + ACCOUNTS_TYPE + "=?";
+ private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
+ + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
+ + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
+ + " AND " + GRANTS_GRANTEE_UID + "=?"
+ + " AND " + ACCOUNTS_NAME + "=?"
+ + " AND " + ACCOUNTS_TYPE + "=?";
+
private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
@@ -376,6 +393,118 @@
}
}
}, UserHandle.ALL, userFilter, null, null);
+
+ LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
+
+ // Need to cancel account request notifications if the update/install can access the account
+ new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ // Called on a handler, and running as the system
+ cancelAccountAccessRequestNotificationIfNeeded(uid, true);
+ }
+
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ // Called on a handler, and running as the system
+ cancelAccountAccessRequestNotificationIfNeeded(uid, true);
+ }
+ }.register(mContext, mMessageHandler.getLooper(), UserHandle.ALL, true);
+
+ // Cancel account request notification if an app op was preventing the account access
+ mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
+ new AppOpsManager.OnOpChangedInternalListener() {
+ @Override
+ public void onOpChanged(int op, String packageName) {
+ try {
+ final int userId = ActivityManager.getCurrentUser();
+ final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ final int mode = mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+ });
+
+ // Cancel account request notification if a permission was preventing the account access
+ mPackageManager.addOnPermissionsChangeListener(
+ (int uid) -> {
+ Account[] accounts = null;
+ String[] packageNames = mPackageManager.getPackagesForUid(uid);
+ if (packageNames != null) {
+ final int userId = UserHandle.getUserId(uid);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (String packageName : packageNames) {
+ if (mContext.getPackageManager().checkPermission(
+ Manifest.permission.GET_ACCOUNTS, packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+
+ if (accounts == null) {
+ accounts = getAccountsAsUser(null, userId, "android");
+ if (ArrayUtils.isEmpty(accounts)) {
+ return;
+ }
+ }
+
+ for (Account account : accounts) {
+ cancelAccountAccessRequestNotificationIfNeeded(
+ account, uid, packageName, true);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ });
+ }
+
+ private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
+ boolean checkAccess) {
+ Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
+ for (Account account : accounts) {
+ cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
+ }
+ }
+
+ private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
+ boolean checkAccess) {
+ Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
+ for (Account account : accounts) {
+ cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
+ }
+ }
+
+ private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
+ boolean checkAccess) {
+ String[] packageNames = mPackageManager.getPackagesForUid(uid);
+ if (packageNames != null) {
+ for (String packageName : packageNames) {
+ cancelAccountAccessRequestNotificationIfNeeded(account, uid,
+ packageName, checkAccess);
+ }
+ }
+ }
+
+ private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
+ int uid, String packageName, boolean checkAccess) {
+ if (!checkAccess || hasAccountAccess(account, packageName,
+ UserHandle.getUserHandleForUid(uid))) {
+ cancelNotification(getCredentialPermissionNotificationId(account,
+ AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
+ UserHandle.getUserHandleForUid(uid));
+ }
}
@Override
@@ -1722,6 +1851,21 @@
} finally {
Binder.restoreCallingIdentity(id);
}
+
+ if (isChanged) {
+ synchronized (accounts.credentialsPermissionNotificationIds) {
+ for (Pair<Pair<Account, String>, Integer> key
+ : accounts.credentialsPermissionNotificationIds.keySet()) {
+ if (account.equals(key.first.first)
+ && AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) {
+ final int uid = (Integer) key.second;
+ mMessageHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
+ account, uid, false));
+ }
+ }
+ }
+ }
+
return isChanged;
}
@@ -2118,7 +2262,7 @@
final int callingUid = getCallingUid();
clearCallingIdentity();
- if (callingUid != Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
throw new SecurityException("can only call from system");
}
int userId = UserHandle.getUserId(callingUid);
@@ -2318,9 +2462,11 @@
if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Intent intent = newGrantCredentialsPermissionIntent(
account,
+ null,
callerUid,
new AccountAuthenticatorResponse(this),
- authTokenType);
+ authTokenType,
+ true);
Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
onResult(bundle);
@@ -2371,7 +2517,7 @@
intent);
doNotification(mAccounts,
account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
- intent, accounts.userId);
+ intent, "android", accounts.userId);
}
}
super.onResult(result);
@@ -2402,7 +2548,7 @@
}
private void createNoCredentialsPermissionNotification(Account account, Intent intent,
- int userId) {
+ String packageName, int userId) {
int uid = intent.getIntExtra(
GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
String authTokenType = intent.getStringExtra(
@@ -2430,20 +2576,23 @@
PendingIntent.FLAG_CANCEL_CURRENT, null, user))
.build();
installNotification(getCredentialPermissionNotificationId(
- account, authTokenType, uid), n, user);
+ account, authTokenType, uid), n, packageName, user.getIdentifier());
}
- private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
- AccountAuthenticatorResponse response, String authTokenType) {
+ private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
+ int uid, AccountAuthenticatorResponse response, String authTokenType,
+ boolean startInNewTask) {
Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
- // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
- // Since it was set in Eclair+ we can't change it without breaking apps using
- // the intent from a non-Activity context.
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addCategory(
- String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
+ if (startInNewTask) {
+ // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
+ // Since it was set in Eclair+ we can't change it without breaking apps using
+ // the intent from a non-Activity context. This is the default behavior.
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
+ authTokenType, uid) + (packageName != null ? packageName : "")));
intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
@@ -3294,6 +3443,123 @@
}
@Override
+ public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
+ @NonNull UserHandle userHandle) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+ throw new SecurityException("Can be called only by system UID");
+ }
+ Preconditions.checkNotNull(account, "account cannot be null");
+ Preconditions.checkNotNull(packageName, "packageName cannot be null");
+ Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
+
+ final int userId = userHandle.getIdentifier();
+
+ Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
+
+ try {
+
+ final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ // Use null token which means any token. Having a token means the package
+ // is trusted by the authenticator, hence it is fine to access the account.
+ if (permissionIsGranted(account, null, uid, userId)) {
+ return true;
+ }
+ // In addition to the permissions required to get an auth token we also allow
+ // the account to be accessed by holders of the get accounts permissions.
+ return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
+ || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private boolean checkUidPermission(String permission, int uid, String opPackageName) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ final int opCode = AppOpsManager.permissionToOpCode(permission);
+ return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
+ opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return false;
+ }
+
+ @Override
+ public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
+ @NonNull String packageName, @NonNull UserHandle userHandle) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+ throw new SecurityException("Can be called only by system UID");
+ }
+
+ Preconditions.checkNotNull(account, "account cannot be null");
+ Preconditions.checkNotNull(packageName, "packageName cannot be null");
+ Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
+
+ final int userId = userHandle.getIdentifier();
+
+ Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
+
+ final int uid;
+ try {
+ uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Unknown package " + packageName);
+ return null;
+ }
+
+ Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return PendingIntent.getActivityAsUser(
+ mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ null, new UserHandle(userId)).getIntentSender();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private Intent newRequestAccountAccessIntent(Account account, String packageName,
+ int uid, RemoteCallback callback) {
+ return newGrantCredentialsPermissionIntent(account, packageName, uid,
+ new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
+ @Override
+ public void onResult(Bundle value) throws RemoteException {
+ handleAuthenticatorResponse(true);
+ }
+
+ @Override
+ public void onRequestContinued() {
+ /* ignore */
+ }
+
+ @Override
+ public void onError(int errorCode, String errorMessage) throws RemoteException {
+ handleAuthenticatorResponse(false);
+ }
+
+ private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
+ cancelNotification(getCredentialPermissionNotificationId(account,
+ AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
+ UserHandle.getUserHandleForUid(uid));
+ if (callback != null) {
+ Bundle result = new Bundle();
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
+ callback.sendResult(result);
+ }
+ }
+ }), AccountManager.ACCOUNT_ACCESS_TOKEN, false);
+ }
+
+ @Override
public boolean someUserHasAccount(@NonNull final Account account) {
if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
throw new SecurityException("Only system can check for accounts across users");
@@ -4933,7 +5199,7 @@
}
private void doNotification(UserAccounts accounts, Account account, CharSequence message,
- Intent intent, int userId) {
+ Intent intent, String packageName, final int userId) {
long identityToken = clearCallingIdentity();
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -4943,12 +5209,12 @@
if (intent.getComponent() != null &&
GrantCredentialsPermissionActivity.class.getName().equals(
intent.getComponent().getClassName())) {
- createNoCredentialsPermissionNotification(account, intent, userId);
+ createNoCredentialsPermissionNotification(account, intent, packageName, userId);
} else {
+ Context contextForUser = getContextForUser(new UserHandle(userId));
final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
intent.addCategory(String.valueOf(notificationId));
- UserHandle user = new UserHandle(userId);
- Context contextForUser = getContextForUser(user);
+
final String notificationTitleFormat =
contextForUser.getText(R.string.notification_title).toString();
Notification n = new Notification.Builder(contextForUser)
@@ -4960,9 +5226,9 @@
.setContentText(message)
.setContentIntent(PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
- null, user))
+ null, new UserHandle(userId)))
.build();
- installNotification(notificationId, n, user);
+ installNotification(notificationId, n, packageName, userId);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -4970,18 +5236,40 @@
}
@VisibleForTesting
- protected void installNotification(final int notificationId, final Notification n,
+ protected void installNotification(int notificationId, final Notification notification,
UserHandle user) {
- ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
- .notifyAsUser(null, notificationId, n, user);
+ installNotification(notificationId, notification, "android", user.getIdentifier());
+ }
+
+ private void installNotification(int notificationId, final Notification notification,
+ String packageName, int userId) {
+ final long token = clearCallingIdentity();
+ try {
+ INotificationManager notificationManager = NotificationManager.getService();
+ try {
+ notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
+ notificationId, notification, new int[1], userId);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@VisibleForTesting
protected void cancelNotification(int id, UserHandle user) {
+ cancelNotification(id, mContext.getPackageName(), user);
+ }
+
+ protected void cancelNotification(int id, String packageName, UserHandle user) {
long identityToken = clearCallingIdentity();
try {
- ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
- .cancelAsUser(null, id, user);
+ INotificationManager service = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
+ } catch (RemoteException e) {
+ /* ignore - local call */
} finally {
restoreCallingIdentity(identityToken);
}
@@ -5042,18 +5330,40 @@
private boolean permissionIsGranted(
Account account, String authTokenType, int callerUid, int userId) {
- final boolean isPrivileged = isPrivileged(callerUid);
- final boolean fromAuthenticator = account != null
- && isAccountManagedByCaller(account.type, callerUid, userId);
- final boolean hasExplicitGrants = account != null
- && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
- + callerUid + ", " + account
- + ": is authenticator? " + fromAuthenticator
- + ", has explicit permission? " + hasExplicitGrants);
+ if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Access to " + account + " granted calling uid is system");
+ }
+ return true;
}
- return fromAuthenticator || hasExplicitGrants || isPrivileged;
+
+ if (isPrivileged(callerUid)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Access to " + account + " granted calling uid "
+ + callerUid + " privileged");
+ }
+ return true;
+ }
+ if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Access to " + account + " granted calling uid "
+ + callerUid + " manages the account");
+ }
+ return true;
+ }
+ if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Access to " + account + " granted calling uid "
+ + callerUid + " user granted access");
+ }
+ return true;
+ }
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
+ }
+
+ return false;
}
private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
@@ -5137,16 +5447,26 @@
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
- if (callerUid == Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
return true;
}
- UserAccounts accounts = getUserAccountsForCaller();
+ UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
synchronized (accounts.cacheLock) {
final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
- String[] args = { String.valueOf(callerUid), authTokenType,
- account.name, account.type};
- final boolean permissionGranted =
- DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
+
+ final String query;
+ final String[] args;
+
+ if (authTokenType != null) {
+ query = COUNT_OF_MATCHING_GRANTS;
+ args = new String[] {String.valueOf(callerUid), authTokenType,
+ account.name, account.type};
+ } else {
+ query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
+ args = new String[] {String.valueOf(callerUid), account.name,
+ account.type};
+ }
+ final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
// TODO: Skip this check when running automated tests. Replace this
// with a more general solution.
@@ -5245,7 +5565,7 @@
throws RemoteException {
final int callingUid = getCallingUid();
- if (callingUid != Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
throw new SecurityException();
}
@@ -5287,6 +5607,8 @@
}
cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
UserHandle.of(accounts.userId));
+
+ cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
}
}
@@ -5604,4 +5926,45 @@
}
}
}
+
+ private final class AccountManagerInternalImpl extends AccountManagerInternal {
+ @Override
+ public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
+ @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
+ if (account == null) {
+ Slog.w(TAG, "account cannot be null");
+ return;
+ }
+ if (packageName == null) {
+ Slog.w(TAG, "packageName cannot be null");
+ return;
+ }
+ if (userId < UserHandle.USER_SYSTEM) {
+ Slog.w(TAG, "user id must be concrete");
+ return;
+ }
+ if (callback == null) {
+ Slog.w(TAG, "callback cannot be null");
+ return;
+ }
+
+ if (hasAccountAccess(account, packageName, new UserHandle(userId))) {
+ Bundle result = new Bundle();
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+ callback.sendResult(result);
+ return;
+ }
+
+ final int uid;
+ try {
+ uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Unknown package " + packageName);
+ return;
+ }
+
+ Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
+ doNotification(mUsers.get(userId), account, null, intent, packageName, userId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9e601eb..1dfca2b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1158,6 +1158,7 @@
* For example, references to the commonly used services.
*/
HashMap<String, IBinder> mAppBindArgs;
+ HashMap<String, IBinder> mIsolatedAppBindArgs;
/**
* Temporary to avoid allocations. Protected by main lock.
@@ -2377,22 +2378,21 @@
if (memInfo != null) {
updateCpuStatsNow();
long nativeTotalPss = 0;
+ final List<ProcessCpuTracker.Stats> stats;
synchronized (mProcessCpuTracker) {
- final int N = mProcessCpuTracker.countStats();
- for (int j=0; j<N; j++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(j);
- if (st.vsize <= 0 || st.uid >= Process.FIRST_APPLICATION_UID) {
- // This is definitely an application process; skip it.
+ stats = mProcessCpuTracker.getStats( (st)-> {
+ return st.vsize > 0 && st.uid < Process.FIRST_APPLICATION_UID;
+ });
+ }
+ final int N = stats.size();
+ for (int j = 0; j < N; j++) {
+ synchronized (mPidsSelfLocked) {
+ if (mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
+ // This is one of our own processes; skip it.
continue;
}
- synchronized (mPidsSelfLocked) {
- if (mPidsSelfLocked.indexOfKey(st.pid) >= 0) {
- // This is one of our own processes; skip it.
- continue;
- }
- }
- nativeTotalPss += Debug.getPss(st.pid, null, null);
}
+ nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
}
memInfo.readMemInfo();
synchronized (ActivityManagerService.this) {
@@ -2935,18 +2935,24 @@
* lazily setup to make sure the services are running when they're asked for.
*/
private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) {
+ // Isolated processes won't get this optimization, so that we don't
+ // violate the rules about which services they have access to.
+ if (isolated) {
+ if (mIsolatedAppBindArgs == null) {
+ mIsolatedAppBindArgs = new HashMap<>();
+ mIsolatedAppBindArgs.put("package", ServiceManager.getService("package"));
+ }
+ return mIsolatedAppBindArgs;
+ }
+
if (mAppBindArgs == null) {
mAppBindArgs = new HashMap<>();
- // Isolated processes won't get this optimization, so that we don't
- // violate the rules about which services they have access to.
- if (!isolated) {
- // Setup the application init args
- mAppBindArgs.put("package", ServiceManager.getService("package"));
- mAppBindArgs.put("window", ServiceManager.getService("window"));
- mAppBindArgs.put(Context.ALARM_SERVICE,
- ServiceManager.getService(Context.ALARM_SERVICE));
- }
+ // Setup the application init args
+ mAppBindArgs.put("package", ServiceManager.getService("package"));
+ mAppBindArgs.put("window", ServiceManager.getService("window"));
+ mAppBindArgs.put(Context.ALARM_SERVICE,
+ ServiceManager.getService(Context.ALARM_SERVICE));
}
return mAppBindArgs;
}
@@ -13813,7 +13819,7 @@
try {
java.lang.Process logcat = new ProcessBuilder(
"/system/bin/timeout", "-k", "15s", "10s",
- "/system/bin/logcat", "-v", "time", "-b", "events", "-b", "system",
+ "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
@@ -16503,21 +16509,24 @@
}
updateCpuStatsNow();
long[] memtrackTmp = new long[1];
+ final List<ProcessCpuTracker.Stats> stats;
+ // Get a list of Stats that have vsize > 0
synchronized (mProcessCpuTracker) {
- final int N = mProcessCpuTracker.countStats();
- for (int i=0; i<N; i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- if (st.vsize > 0) {
- long pss = Debug.getPss(st.pid, null, memtrackTmp);
- if (pss > 0) {
- if (infoMap.indexOfKey(st.pid) < 0) {
- ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid,
- ProcessList.NATIVE_ADJ, -1, "native", null);
- mi.pss = pss;
- mi.memtrack = memtrackTmp[0];
- memInfos.add(mi);
- }
- }
+ stats = mProcessCpuTracker.getStats((st) -> {
+ return st.vsize > 0;
+ });
+ }
+ final int statsCount = stats.size();
+ for (int i = 0; i < statsCount; i++) {
+ ProcessCpuTracker.Stats st = stats.get(i);
+ long pss = Debug.getPss(st.pid, null, memtrackTmp);
+ if (pss > 0) {
+ if (infoMap.indexOfKey(st.pid) < 0) {
+ ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid,
+ ProcessList.NATIVE_ADJ, -1, "native", null);
+ mi.pss = pss;
+ mi.memtrack = memtrackTmp[0];
+ memInfos.add(mi);
}
}
}
@@ -17770,6 +17779,7 @@
|| Intent.ACTION_MEDIA_BUTTON.equals(action)
|| Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
|| Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
+ || Intent.ACTION_MASTER_CLEAR.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
|| LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8c4c0ad..2722e275 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1591,11 +1591,6 @@
return STACK_INVISIBLE;
}
- final boolean isLockscreenShown = mService.mLockScreenShown == LOCK_SCREEN_SHOWN;
- if (isLockscreenShown && !StackId.isAllowedOverLockscreen(mStackId)) {
- return STACK_INVISIBLE;
- }
-
final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
final int focusedStackId = focusedStack.mStackId;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 5252446..844d467 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -103,6 +103,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManagerInternal;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -132,6 +133,9 @@
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+ // TODO b/30204367 remove when the platform fully supports ephemeral applications
+ private static final boolean USE_DEFAULT_EPHEMERAL_LAUNCHER = false;
+
private final ActivityManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private ActivityStartInterceptor mInterceptor;
@@ -456,39 +460,13 @@
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
- // Create a pending intent to start the intent resolved here.
- final IIntentSender failureTarget = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
- new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
-
- // Create a pending intent to start the ephemeral application; force it to be
- // directed to the ephemeral package.
- ephemeralIntent.setPackage(rInfo.ephemeralResolveInfo.getPackageName());
- final IIntentSender ephemeralTarget = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ ephemeralIntent },
- new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
-
- int flags = intent.getFlags();
- intent = new Intent();
- intent.setFlags(flags
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
- rInfo.ephemeralResolveInfo.getPackageName());
- intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureTarget));
- intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(ephemeralTarget));
-
+ intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
+ rInfo.ephemeralResolveInfo.getPackageName(), callingPackage, resolvedType,
+ userId);
resolvedType = null;
callingUid = realCallingUid;
callingPid = realCallingPid;
- rInfo = rInfo.ephemeralInstaller;
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
}
@@ -543,6 +521,60 @@
return err;
}
+ /**
+ * Builds and returns an intent to launch the ephemeral installer.
+ */
+ private Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
+ String ephemeralPackage, String callingPackage, String resolvedType, int userId) {
+ final Intent nonEphemeralIntent = new Intent(origIntent);
+ nonEphemeralIntent.setFlags(nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
+ // Intent that is launched if the ephemeral package couldn't be installed
+ // for any reason.
+ final IIntentSender failureIntentTarget = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null /*token*/, null /*resultWho*/, 1,
+ new Intent[]{ nonEphemeralIntent }, new String[]{ resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/);
+
+ final Intent ephemeralIntent;
+ if (USE_DEFAULT_EPHEMERAL_LAUNCHER) {
+ // Force the intent to be directed to the ephemeral package
+ ephemeralIntent = new Intent(origIntent);
+ ephemeralIntent.setPackage(ephemeralPackage);
+ } else {
+ // Success intent goes back to the installer
+ // TODO; do we need any extras for the installer?
+ ephemeralIntent = new Intent(launchIntent);
+ ephemeralIntent.setData(null);
+ }
+
+ // Intent that is eventually launched if the ephemeral package was
+ // installed successfully. This will actually be launched by a platform
+ // broadcast receiver.
+ final IIntentSender successIntentTarget = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null /*token*/, null /*resultWho*/, 0,
+ new Intent[]{ ephemeralIntent }, new String[]{ resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/);
+
+ // Finally build the actual intent to launch the ephemeral installer
+ int flags = launchIntent.getFlags();
+ final Intent intent = new Intent();
+ intent.setFlags(flags
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackage);
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureIntentTarget));
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(successIntentTarget));
+ // TODO: Remove when the platform has fully implemented ephemeral apps
+ intent.setData(origIntent.getData());
+ return intent;
+ }
+
void postStartActivityUncheckedProcessing(
ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
ActivityStack targetStack) {
@@ -572,6 +604,9 @@
// If we launched the activity from a no display activity that was launched from the home
// screen, we also need to start recents to un-minimize the docked stack, since the
// noDisplay activity will be finished shortly after.
+ // Note that some apps have trampoline activities without noDisplay being set. In that case,
+ // we have another heuristic in DockedStackDividerController.notifyAppTransitionStarting
+ // that tries to detect that case.
// TODO: We should prevent noDisplay activities from affecting task/stack ordering and
// visibility instead of using this flag.
final boolean noDisplayActivityOverHome = sourceRecord != null
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 7eff773..576f2b2 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -575,6 +575,8 @@
boolean handleAppCrashLocked(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
long now = SystemClock.uptimeMillis();
+ boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
Long crashTime;
Long crashTimePersistent;
@@ -612,7 +614,9 @@
// processes run critical code.
mService.removeProcessLocked(app, false, false, "crash");
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
- return false;
+ if (!showBackground) {
+ return false;
+ }
}
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
@@ -705,7 +709,7 @@
}
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
- if (mService.canShowErrorDialogs() && !crashSilenced) {
+ if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced) {
proc.crashDialog = new AppErrorDialog(mContext, mService, data);
} else {
// The device is asleep, so just pretend that the user
@@ -942,7 +946,9 @@
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
- if (mService.canShowErrorDialogs()) {
+ boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+ if (mService.canShowErrorDialogs() || showBackground) {
d = new AppNotRespondingDialog(mService,
mContext, proc, (ActivityRecord)data.get("activity"),
msg.arg1 != 0);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 2467a90..9c08453 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -38,6 +38,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.Objects;
final class PendingIntentRecord extends IIntentSender.Stub {
private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM;
@@ -102,7 +103,7 @@
if (requestResolvedType != null) {
hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode();
}
- hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode();
+ hash = (ODD_PRIME_NUMBER*hash) + (_p != null ? _p.hashCode() : 0);
hash = (ODD_PRIME_NUMBER*hash) + _t;
hashCode = hash;
//Slog.i(ActivityManagerService.TAG, this + " hashCode=0x"
@@ -121,20 +122,14 @@
if (userId != other.userId){
return false;
}
- if (!packageName.equals(other.packageName)) {
+ if (!Objects.equals(packageName, other.packageName)) {
return false;
}
if (activity != other.activity) {
return false;
}
- if (who != other.who) {
- if (who != null) {
- if (!who.equals(other.who)) {
- return false;
- }
- } else if (other.who != null) {
- return false;
- }
+ if (!Objects.equals(who, other.who)) {
+ return false;
}
if (requestCode != other.requestCode) {
return false;
@@ -148,14 +143,8 @@
return false;
}
}
- if (requestResolvedType != other.requestResolvedType) {
- if (requestResolvedType != null) {
- if (!requestResolvedType.equals(other.requestResolvedType)) {
- return false;
- }
- } else if (other.requestResolvedType != null) {
- return false;
- }
+ if (!Objects.equals(requestResolvedType, other.requestResolvedType)) {
+ return false;
}
if (flags != other.flags) {
return false;
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 48fecd5..43eb251 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -87,6 +87,8 @@
private final RecentTasks mRecentTasks;
private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
private final File mTaskIdsDir;
+ // To lock file operations in TaskPersister
+ private final Object mIoLock = new Object();
/**
* Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
@@ -195,52 +197,52 @@
return mTaskIdsInFile.get(userId).clone();
}
final SparseBooleanArray persistedTaskIds = new SparseBooleanArray();
- BufferedReader reader = null;
- String line;
- try {
- reader = new BufferedReader(new FileReader(getUserPersistedTaskIdsFile(userId)));
- while ((line = reader.readLine()) != null) {
- for (String taskIdString : line.split("\\s+")) {
- int id = Integer.parseInt(taskIdString);
- persistedTaskIds.put(id, true);
+ synchronized (mIoLock) {
+ BufferedReader reader = null;
+ String line;
+ try {
+ reader = new BufferedReader(new FileReader(getUserPersistedTaskIdsFile(userId)));
+ while ((line = reader.readLine()) != null) {
+ for (String taskIdString : line.split("\\s+")) {
+ int id = Integer.parseInt(taskIdString);
+ persistedTaskIds.put(id, true);
+ }
}
+ } catch (FileNotFoundException e) {
+ // File doesn't exist. Ignore.
+ } catch (Exception e) {
+ Slog.e(TAG, "Error while reading taskIds file for user " + userId, e);
+ } finally {
+ IoUtils.closeQuietly(reader);
}
- } catch (FileNotFoundException e) {
- // File doesn't exist. Ignore.
- } catch (Exception e) {
- Slog.e(TAG, "Error while reading taskIds file for user " + userId, e);
- } finally {
- IoUtils.closeQuietly(reader);
}
mTaskIdsInFile.put(userId, persistedTaskIds);
return persistedTaskIds.clone();
}
+
@VisibleForTesting
- void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) {
+ void writePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) {
if (userId < 0) {
return;
}
- SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
- if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIds)) {
- return;
- }
final File persistedTaskIdsFile = getUserPersistedTaskIdsFile(userId);
- BufferedWriter writer = null;
- try {
- writer = new BufferedWriter(new FileWriter(persistedTaskIdsFile));
- for (int i = 0; i < taskIds.size(); i++) {
- if (taskIds.valueAt(i)) {
- writer.write(String.valueOf(taskIds.keyAt(i)));
- writer.newLine();
+ synchronized (mIoLock) {
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(persistedTaskIdsFile));
+ for (int i = 0; i < taskIds.size(); i++) {
+ if (taskIds.valueAt(i)) {
+ writer.write(String.valueOf(taskIds.keyAt(i)));
+ writer.newLine();
+ }
}
+ } catch (Exception e) {
+ Slog.e(TAG, "Error while writing taskIds file for user " + userId, e);
+ } finally {
+ IoUtils.closeQuietly(writer);
}
- } catch (Exception e) {
- Slog.e(TAG, "Error while writing taskIds file for user " + userId, e);
- } finally {
- IoUtils.closeQuietly(writer);
}
- mTaskIdsInFile.put(userId, taskIds.clone());
}
void unloadUserDataFromMemory(int userId) {
@@ -543,16 +545,23 @@
}
private void writeTaskIdsFiles() {
- int candidateUserIds[];
+ SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
synchronized (mService) {
- candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
- }
- SparseBooleanArray taskIdsToSave;
- for (int userId : candidateUserIds) {
- synchronized (mService) {
- taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId).clone();
+ for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
+ SparseBooleanArray taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId);
+ SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
+ if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) {
+ continue;
+ } else {
+ SparseBooleanArray taskIdsToSaveCopy = taskIdsToSave.clone();
+ mTaskIdsInFile.put(userId, taskIdsToSaveCopy);
+ changedTaskIdsPerUser.put(userId, taskIdsToSaveCopy);
+ }
}
- maybeWritePersistedTaskIdsForUser(taskIdsToSave, userId);
+ }
+ for (int i = 0; i < changedTaskIdsPerUser.size(); i++) {
+ writePersistedTaskIdsForUser(changedTaskIdsPerUser.valueAt(i),
+ changedTaskIdsPerUser.keyAt(i));
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index 7525f30..c2c1a8c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -25,6 +25,7 @@
import android.net.RouteInfo;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.util.NetdService;
import android.os.INetworkManagementService;
import android.os.ServiceSpecificException;
import android.os.RemoteException;
@@ -193,7 +194,7 @@
private void configureLocalDns(
HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
- INetd netd = getNetdServiceOrNull();
+ final INetd netd = NetdService.getInstance();
if (netd == null) {
if (newDnses != null) newDnses.clear();
Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
@@ -265,18 +266,6 @@
return localRoutes;
}
- private INetd getNetdServiceOrNull() {
- if (mNMService != null) {
- try {
- return mNMService.getNetdService();
- } catch (RemoteException ignored) {
- // This blocks until netd can be reached, but it can return
- // null during a netd crash.
- }
- }
- return null;
- }
-
// Given a prefix like 2001:db8::/64 return 2001:db8::1.
private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
final byte[] dnsBytes = localPrefix.getRawAddress();
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 01b2393..12955f5 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -482,7 +482,6 @@
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleSync(account, userId, uId, authority, extras,
- 0 /* no delay */, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
}
} finally {
@@ -547,11 +546,8 @@
getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
flextime, extras);
} else {
- long beforeRuntimeMillis = (flextime) * 1000;
- long runtimeMillis = runAtTime * 1000;
syncManager.scheduleSync(
request.getAccount(), userId, callerUid, request.getProvider(), extras,
- beforeRuntimeMillis, runtimeMillis,
false /* onlyThoseWithUnknownSyncableState */);
}
} finally {
@@ -841,7 +837,7 @@
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getIsSyncable(
+ return syncManager.computeSyncable(
account, userId, providerName);
}
} finally {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 39ddc3a..f739fa8 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -19,6 +19,7 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerInternal;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -64,6 +65,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -79,6 +81,7 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerInternal;
import com.google.android.collect.Lists;
@@ -133,6 +136,8 @@
public class SyncManager {
static final String TAG = "SyncManager";
+ private static final boolean DEBUG_ACCOUNT_ACCESS = false;
+
/** Delay a sync due to local changes this long. In milliseconds */
private static final long LOCAL_SYNC_DELAY;
@@ -194,6 +199,11 @@
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
+
+ private static final int SYNC_OP_STATE_VALID = 0;
+ private static final int SYNC_OP_STATE_INVALID = 1;
+ private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2;
+
private Context mContext;
private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
@@ -310,6 +320,10 @@
private final UserManager mUserManager;
+ private final AccountManager mAccountManager;
+
+ private final AccountManagerInternal mAccountManagerInternal;
+
private List<UserInfo> getAllUsers() {
return mUserManager.getUsers();
}
@@ -490,8 +504,6 @@
@Override
public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
scheduleSync(info.account, info.userId, reason, info.provider, extras,
- 0 /* no flexMillis */,
- 0 /* run immediately */,
false);
}
});
@@ -522,8 +534,7 @@
if (!removed) {
scheduleSync(null, UserHandle.USER_ALL,
SyncOperation.REASON_SERVICE_CHANGED,
- type.authority, null, 0 /* no delay */, 0 /* no delay */,
- false /* onlyThoseWithUnkownSyncableState */);
+ type.authority, null, false /* onlyThoseWithUnkownSyncableState */);
}
}
}, mSyncHandler);
@@ -562,6 +573,9 @@
}
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
+ mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
+
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
@@ -655,7 +669,7 @@
return mSyncStorageEngine;
}
- public int getIsSyncable(Account account, int userId, String providerName) {
+ private int getIsSyncable(Account account, int userId, String providerName) {
int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
@@ -666,22 +680,22 @@
RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(
SyncAdapterType.newKey(providerName, account.type), userId);
- if (syncAdapterInfo == null) return isSyncable;
+ if (syncAdapterInfo == null) return AuthorityInfo.NOT_SYNCABLE;
PackageInfo pInfo = null;
try {
pInfo = AppGlobals.getPackageManager().getPackageInfo(
syncAdapterInfo.componentName.getPackageName(), 0, userId);
- if (pInfo == null) return isSyncable;
+ if (pInfo == null) return AuthorityInfo.NOT_SYNCABLE;
} catch (RemoteException re) {
// Shouldn't happen.
- return isSyncable;
+ return AuthorityInfo.NOT_SYNCABLE;
}
if (pInfo.restrictedAccountType != null
&& pInfo.restrictedAccountType.equals(account.type)) {
return isSyncable;
} else {
- return 0;
+ return AuthorityInfo.NOT_SYNCABLE;
}
}
@@ -733,13 +747,10 @@
* @param extras a Map of SyncAdapter-specific information to control
* syncs of a specific provider. Can be null. Is ignored
* if the url is null.
- * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run.
- * @param runtimeMillis maximum milliseconds in the future to wait before performing sync.
* @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
- long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
+ String requestedAuthority, Bundle extras, boolean onlyThoseWithUnkownSyncableState) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (extras == null) {
extras = new Bundle();
@@ -749,17 +760,27 @@
+ requestedAuthority);
}
- AccountAndUser[] accounts;
- if (requestedAccount != null && userId != UserHandle.USER_ALL) {
- accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
+ AccountAndUser[] accounts = null;
+ if (requestedAccount != null) {
+ if (userId != UserHandle.USER_ALL) {
+ accounts = new AccountAndUser[]{new AccountAndUser(requestedAccount, userId)};
+ } else {
+ for (AccountAndUser runningAccount : mRunningAccounts) {
+ if (requestedAccount.equals(runningAccount.account)) {
+ accounts = ArrayUtils.appendElement(AccountAndUser.class,
+ accounts, runningAccount);
+ }
+ }
+ }
} else {
accounts = mRunningAccounts;
- if (accounts.length == 0) {
- if (isLoggable) {
- Slog.v(TAG, "scheduleSync: no accounts configured, dropping");
- }
- return;
+ }
+
+ if (ArrayUtils.isEmpty(accounts)) {
+ if (isLoggable) {
+ Slog.v(TAG, "scheduleSync: no accounts configured, dropping");
}
+ return;
}
final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
@@ -808,29 +829,41 @@
}
for (String authority : syncableAuthorities) {
- int isSyncable = getIsSyncable(account.account, account.userId,
- authority);
+ int isSyncable = computeSyncable(account.account, account.userId, authority);
+
if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
continue;
}
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(authority, account.account.type), account.userId);
+
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(SyncAdapterType.newKey(authority,
+ account.account.type), account.userId);
if (syncAdapterInfo == null) {
continue;
}
+
final int owningUid = syncAdapterInfo.uid;
- final String owningPackage = syncAdapterInfo.componentName.getPackageName();
- try {
- if (ActivityManagerNative.getDefault().getAppStartMode(owningUid,
- owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
- + syncAdapterInfo.componentName
- + " -- package not allowed to start");
- continue;
+
+ if (isSyncable == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
+ if (isLoggable) {
+ Slog.v(TAG, " Not scheduling sync operation: "
+ + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
}
- } catch (RemoteException e) {
+ Bundle finalExtras = new Bundle(extras);
+ mAccountManagerInternal.requestAccountAccess(account.account,
+ syncAdapterInfo.componentName.getPackageName(),
+ UserHandle.getUserId(owningUid),
+ new RemoteCallback((Bundle result) -> {
+ if (result != null
+ && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
+ scheduleSync(account.account, userId, reason, authority,
+ finalExtras, onlyThoseWithUnkownSyncableState);
+ }
+ }
+ ));
+ continue;
}
+
final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
if (isSyncable < 0 && isAlwaysSyncable) {
@@ -838,6 +871,7 @@
account.account, account.userId, authority, AuthorityInfo.SYNCABLE);
isSyncable = AuthorityInfo.SYNCABLE;
}
+
if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
continue;
}
@@ -863,6 +897,9 @@
account.account, authority, account.userId);
long delayUntil =
mSyncStorageEngine.getDelayUntilTime(info);
+
+ final String owningPackage = syncAdapterInfo.componentName.getPackageName();
+
if (isSyncable < 0) {
// Initialisation sync.
Bundle newExtras = new Bundle();
@@ -887,8 +924,6 @@
if (isLoggable) {
Slog.v(TAG, "scheduleSync:"
+ " delay until " + delayUntil
- + " run by " + runtimeMillis
- + " flexMillis " + beforeRuntimeMillis
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
@@ -904,6 +939,56 @@
}
}
+ public int computeSyncable(Account account, int userId, String authority) {
+ final int status = getIsSyncable(account, userId, authority);
+ if (status == AuthorityInfo.NOT_SYNCABLE) {
+ return AuthorityInfo.NOT_SYNCABLE;
+ }
+ final SyncAdapterType type = SyncAdapterType.newKey(authority, account.type);
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(type, userId);
+ if (syncAdapterInfo == null) {
+ return AuthorityInfo.NOT_SYNCABLE;
+ }
+ final int owningUid = syncAdapterInfo.uid;
+ final String owningPackage = syncAdapterInfo.componentName.getPackageName();
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(owningUid,
+ owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
+ + syncAdapterInfo.componentName
+ + " -- package not allowed to start");
+ return AuthorityInfo.NOT_SYNCABLE;
+ }
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
+ if (!canAccessAccount(account, owningPackage, owningUid)) {
+ Log.w(TAG, "Access to " + account + " denied for package "
+ + owningPackage + " in UID " + syncAdapterInfo.uid);
+ return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
+ }
+
+ return status;
+ }
+
+ private boolean canAccessAccount(Account account, String packageName, int uid) {
+ if (mAccountManager.hasAccountAccess(account, packageName,
+ UserHandle.getUserHandleForUid(uid))) {
+ return true;
+ }
+ // We relax the account access rule to also include the system apps as
+ // they are trusted and we want to minimize the cases where the user
+ // involvement is required to grant access to the synced account.
+ try {
+ mContext.getPackageManager().getApplicationInfoAsUser(packageName,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.getUserId(uid));
+ return true;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
private void removeSyncsForAuthority(EndPoint info) {
verifyJobScheduler();
List<SyncOperation> ops = getAllPendingSyncs();
@@ -960,8 +1045,6 @@
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
scheduleSync(account, userId, reason, authority, extras,
- LOCAL_SYNC_DELAY /* earliest run time */,
- 2 * LOCAL_SYNC_DELAY /* latest sync time. */,
false /* onlyThoseWithUnkownSyncableState */);
}
@@ -1421,7 +1504,6 @@
mContext.getOpPackageName());
for (Account account : accounts) {
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
- 0 /* no delay */, 0 /* No flexMillis */,
true /* onlyThoseWithUnknownSyncableState */);
}
}
@@ -2530,13 +2612,18 @@
}
}
- if (isOperationValid(op)) {
- if (!dispatchSyncOperation(op)) {
+ final int syncOpState = computeSyncOpState(op);
+ switch (syncOpState) {
+ case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
+ case SYNC_OP_STATE_INVALID: {
mSyncJobService.callJobFinished(op.jobId, false);
- }
- } else {
+ } return;
+ }
+
+ if (!dispatchSyncOperation(op)) {
mSyncJobService.callJobFinished(op.jobId, false);
}
+
setAuthorityPendingState(op.target);
}
@@ -2596,8 +2683,7 @@
if (syncTargets != null) {
scheduleSync(syncTargets.account, syncTargets.userId,
- SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, 0, 0,
- true);
+ SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, true);
}
}
@@ -2665,6 +2751,26 @@
SyncStorageEngine.SOURCE_PERIODIC, extras,
syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
pollFrequencyMillis, flexMillis);
+
+ final int syncOpState = computeSyncOpState(op);
+ switch (syncOpState) {
+ case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: {
+ mAccountManagerInternal.requestAccountAccess(op.target.account,
+ op.owningPackage, UserHandle.getUserId(op.owningUid),
+ new RemoteCallback((Bundle result) -> {
+ if (result != null
+ && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
+ updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+ }
+ }
+ ));
+ } return;
+
+ case SYNC_OP_STATE_INVALID: {
+ return;
+ }
+ }
+
scheduleSyncOperationH(op);
mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
@@ -2725,29 +2831,38 @@
/**
* Determine if a sync is no longer valid and should be dropped.
*/
- private boolean isOperationValid(SyncOperation op) {
+ private int computeSyncOpState(SyncOperation op) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
int state;
final EndPoint target = op.target;
- boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId);
+
// Drop the sync if the account of this operation no longer exists.
AccountAndUser[] accounts = mRunningAccounts;
if (!containsAccountAndUser(accounts, target.account, target.userId)) {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
}
- return false;
+ return SYNC_OP_STATE_INVALID;
}
// Drop this sync request if it isn't syncable.
- state = getIsSyncable(target.account, target.userId, target.provider);
- if (state == 0) {
+ state = computeSyncable(target.account, target.userId, target.provider);
+ if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
if (isLoggable) {
- Slog.v(TAG, " Dropping sync operation: isSyncable == 0.");
+ Slog.v(TAG, " Dropping sync operation: "
+ + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
}
- return false;
+ return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS;
}
- syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically(
- target.account, target.userId, target.provider);
+ if (state == AuthorityInfo.NOT_SYNCABLE) {
+ if (isLoggable) {
+ Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE");
+ }
+ return SYNC_OP_STATE_INVALID;
+ }
+
+ final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
+ && mSyncStorageEngine.getSyncAutomatically(target.account,
+ target.userId, target.provider);
// We ignore system settings that specify the sync is invalid if:
// 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
@@ -2760,9 +2875,9 @@
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: disallowed by settings/network.");
}
- return false;
+ return SYNC_OP_STATE_INVALID;
}
- return true;
+ return SYNC_OP_STATE_VALID;
}
private boolean dispatchSyncOperation(SyncOperation op) {
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index bc3fc6a..64849aa 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -234,6 +234,12 @@
*/
public static final int SYNCABLE_NOT_INITIALIZED = 2;
+ /**
+ * The adapter is syncable but does not have access to the synced account and needs a
+ * user access approval.
+ */
+ public static final int SYNCABLE_NO_ACCOUNT_ACCESS = 3;
+
final EndPoint target;
final int ident;
boolean enabled;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0abd2e7..45a9644 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1402,6 +1402,9 @@
throw new IllegalArgumentException("width, height, and densityDpi must be "
+ "greater than 0");
}
+ if (surface.isSingleBuffered()) {
+ throw new IllegalArgumentException("Surface can't be single-buffered");
+ }
if ((flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index 07fa2ce..e7fd3d8 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -182,6 +182,8 @@
}
private void setUp() {
+ Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
+
// Create a new controller for the current user and start listening for changes.
mController = new NightDisplayController(getContext(), mCurrentUser);
mController.setListener(this);
@@ -196,6 +198,8 @@
}
private void tearDown() {
+ Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
+
if (mController != null) {
mController.setListener(null);
mController = null;
@@ -273,6 +277,8 @@
@Override
public void onAutoModeChanged(int autoMode) {
+ Slog.d(TAG, "onAutoModeChanged: autoMode=" + autoMode);
+
if (mAutoMode != null) {
mAutoMode.onStop();
mAutoMode = null;
@@ -291,6 +297,8 @@
@Override
public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
+
if (mAutoMode != null) {
mAutoMode.onCustomStartTimeChanged(startTime);
}
@@ -298,6 +306,8 @@
@Override
public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
+
if (mAutoMode != null) {
mAutoMode.onCustomEndTimeChanged(endTime);
}
@@ -419,7 +429,7 @@
@Override
public void onAlarm() {
- if (DEBUG) Slog.d(TAG, "onAlarm");
+ Slog.d(TAG, "onAlarm");
updateActivated();
}
}
@@ -477,7 +487,8 @@
@Override
public void onTwilightStateChanged(@Nullable TwilightState state) {
- if (DEBUG) Slog.d(TAG, "onTwilightStateChanged");
+ Slog.d(TAG, "onTwilightStateChanged: isNight="
+ + (state == null ? null : state.isNight()));
updateActivated(state);
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 777eee8..697186e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -78,6 +78,8 @@
public class MediaSessionService extends SystemService implements Monitor {
private static final String TAG = "MediaSessionService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // Leave log for media key event always.
+ private static final boolean DEBUG_MEDIA_KEY_EVENT = DEBUG || true;
private static final int WAKELOCK_TIMEOUT = 5000;
@@ -302,7 +304,7 @@
*/
private void destroySessionLocked(MediaSessionRecord session) {
if (DEBUG) {
- Log.d(TAG, "Destroying session : " + session.toString());
+ Log.d(TAG, "Destroying " + session);
}
int userId = session.getUserId();
UserRecord user = mUserRecords.get(userId);
@@ -408,7 +410,7 @@
if (component != null) {
if (compName.equals(component)) {
if (DEBUG) {
- Log.d(TAG, "ok to get sessions: " + component +
+ Log.d(TAG, "ok to get sessions. " + component +
" is authorized notification listener");
}
return true;
@@ -417,7 +419,7 @@
}
}
if (DEBUG) {
- Log.d(TAG, "not ok to get sessions, " + compName +
+ Log.d(TAG, "not ok to get sessions. " + compName +
" is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
}
}
@@ -462,7 +464,7 @@
mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
if (DEBUG) {
- Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
+ Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
}
return session;
}
@@ -881,17 +883,16 @@
private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
MediaSessionRecord session) {
- if (DEBUG) {
- String description = session == null ? null : session.toString();
- Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
- + flags + ", suggestedStream=" + suggestedStream);
-
- }
boolean preferSuggestedStream = false;
if (isValidLocalStreamType(suggestedStream)
&& AudioSystem.isStreamActive(suggestedStream, 0)) {
preferSuggestedStream = true;
}
+ if (DEBUG) {
+ Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
+ + flags + ", suggestedStream=" + suggestedStream
+ + ", preferSuggestedStream=" + preferSuggestedStream);
+ }
if (session == null || preferSuggestedStream) {
if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
&& !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
@@ -946,8 +947,8 @@
private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
MediaSessionRecord session) {
if (session != null) {
- if (DEBUG) {
- Log.d(TAG, "Sending media key to " + session.toString());
+ if (DEBUG_MEDIA_KEY_EVENT) {
+ Log.d(TAG, "Sending " + keyEvent + " to " + session);
}
if (needWakeLock) {
mKeyEventReceiver.aquireWakeLockLocked();
@@ -966,11 +967,6 @@
&& user.mRestoredMediaButtonReceiver == null) {
continue;
}
- if (DEBUG) {
- Log.d(TAG, "Sending media key to last known PendingIntent "
- + user.mLastMediaButtonReceiver + " or restored Intent "
- + user.mRestoredMediaButtonReceiver);
- }
if (needWakeLock) {
mKeyEventReceiver.aquireWakeLockLocked();
}
@@ -979,10 +975,19 @@
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
try {
if (user.mLastMediaButtonReceiver != null) {
+ if (DEBUG_MEDIA_KEY_EVENT) {
+ Log.d(TAG, "Sending " + keyEvent
+ + " to the last known pendingIntent "
+ + user.mLastMediaButtonReceiver);
+ }
user.mLastMediaButtonReceiver.send(getContext(),
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mediaButtonIntent, mKeyEventReceiver, mHandler);
} else {
+ if (DEBUG_MEDIA_KEY_EVENT) {
+ Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
+ + user.mRestoredMediaButtonReceiver);
+ }
mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
getContext().sendBroadcastAsUser(mediaButtonIntent,
UserHandle.of(userId));
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f3fc676..d5da66b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -513,12 +513,13 @@
try {
app = pm.getApplicationInfoAsUser(pkg, PackageManager.MATCH_SYSTEM_ONLY, userId);
} catch (PackageManager.NameNotFoundException e) {
- // Should not happen
- Slog.wtf(TAG, "No ApplicationInfo for package " + pkg);
+ if (LOGD) Slog.d(TAG, "No ApplicationInfo for package " + pkg);
+ // Ignore it - some apps on allow-in-data-usage-save are optional.
continue;
}
if (!app.isPrivilegedApp()) {
- Slog.wtf(TAG, "pm.getApplicationInfoAsUser() returned non-privileged app: " + pkg);
+ Slog.e(TAG, "addDefaultRestrictBackgroundWhitelistUidsUL(): "
+ + "skipping non-privileged app " + pkg);
continue;
}
final int uid = UserHandle.getUid(userId, app.uid);
@@ -528,8 +529,9 @@
+ "background whitelist. Revoked status: "
+ mRestrictBackgroundWhitelistRevokedUids.get(uid));
if (!mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
- Slog.i(TAG, "adding default package " + pkg + " (uid " + uid + " for user "
- + userId + ") to restrict background whitelist");
+ if (LOGD)
+ Slog.d(TAG, "adding default package " + pkg + " (uid " + uid + " for user "
+ + userId + ") to restrict background whitelist");
mRestrictBackgroundWhitelistUids.append(uid, true);
changed = true;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 6a56fa6..d25abbf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -870,13 +870,8 @@
IntentSender statusReceiver, int userId) {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
- boolean allowSilentUninstall = true;
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
- final String installerPackageName = mPm.getInstallerPackageName(packageName);
- allowSilentUninstall = mPm.isOrphaned(packageName) ||
- (installerPackageName != null
- && installerPackageName.equals(callerPackageName));
}
// Check whether the caller is device owner, in which case we do it silently.
@@ -887,8 +882,8 @@
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, packageName, isDeviceOwner, userId);
- if (allowSilentUninstall && mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
+ == PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
} else if (isDeviceOwner) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6cdc40f..5831284 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -109,6 +109,7 @@
final int installerUid;
final SessionParams params;
final long createdMillis;
+ final int defaultContainerGid;
/** Staging location where client data is written. */
final File stageDir;
@@ -199,13 +200,19 @@
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
+ // Cache package manager data without the lock held
+ final PackageInfo pkgInfo = mPm.getPackageInfo(
+ params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
+ final ApplicationInfo appInfo = mPm.getApplicationInfo(
+ params.appPackageName, 0, userId);
+
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
- commitLocked();
+ commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -264,6 +271,9 @@
} else {
mPermissionsAccepted = false;
}
+ final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ defaultContainerGid = UserHandle.getSharedAppGid(uid);
}
public SessionInfo generateInfo() {
@@ -520,7 +530,8 @@
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
- private void commitLocked() throws PackageManagerException {
+ private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
+ throws PackageManagerException {
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
}
@@ -538,7 +549,7 @@
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
- validateInstallLocked();
+ validateInstallLocked(pkgInfo, appInfo);
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
@@ -650,7 +661,8 @@
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
- private void validateInstallLocked() throws PackageManagerException {
+ private void validateInstallLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
+ throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
@@ -729,10 +741,8 @@
if (removeSplitList.size() > 0) {
// validate split names marked for removal
- final int flags = mSignatures == null ? PackageManager.GET_SIGNATURES : 0;
- final PackageInfo pkg = mPm.getPackageInfo(params.appPackageName, flags, userId);
for (String splitName : removeSplitList) {
- if (!ArrayUtils.contains(pkg.splitNames, splitName)) {
+ if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split not found: " + splitName);
}
@@ -740,11 +750,11 @@
// ensure we've got appropriate package name, version code and signatures
if (mPackageName == null) {
- mPackageName = pkg.packageName;
- mVersionCode = pkg.versionCode;
+ mPackageName = pkgInfo.packageName;
+ mVersionCode = pkgInfo.versionCode;
}
if (mSignatures == null) {
- mSignatures = pkg.signatures;
+ mSignatures = pkgInfo.signatures;
}
}
@@ -757,8 +767,7 @@
} else {
// Partial installs must be consistent with existing install
- final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
- if (app == null) {
+ if (appInfo == null) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package for " + mPackageName);
}
@@ -766,8 +775,8 @@
final PackageLite existing;
final ApkLite existingBase;
try {
- existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
- existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
+ existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
+ existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
@@ -777,7 +786,7 @@
// Inherit base if not overridden
if (mResolvedBaseFile == null) {
- mResolvedBaseFile = new File(app.getBaseCodePath());
+ mResolvedBaseFile = new File(appInfo.getBaseCodePath());
mResolvedInheritedFiles.add(mResolvedBaseFile);
}
@@ -794,7 +803,7 @@
}
// Inherit compiled oat directory.
- final File packageInstallDir = (new File(app.getBaseCodePath())).getParentFile();
+ final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
mInheritedFilesBase = packageInstallDir;
final File oatDir = new File(packageInstallDir, "oat");
if (oatDir.exists()) {
@@ -822,7 +831,8 @@
}
}
- private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
+ private void assertApkConsistent(String tag, ApkLite apk)
+ throws PackageManagerException {
if (!mPackageName.equals(apk.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
+ apk.packageName + " inconsistent with " + mPackageName);
@@ -1035,10 +1045,7 @@
"Failed to finalize container " + cid);
}
- final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
- final int gid = UserHandle.getSharedAppGid(uid);
- if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
+ if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to fix permissions on container " + cid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f326555..f84356b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -177,6 +177,7 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -196,6 +197,7 @@
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.system.ErrnoException;
@@ -363,6 +365,7 @@
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
+ // STOPSHIP; b/30256615
private static final boolean DISABLE_EPHEMERAL_APPS = !Build.IS_DEBUGGABLE;
private static final int RADIO_UID = Process.PHONE_UID;
@@ -531,6 +534,7 @@
final String[] mSeparateProcesses;
final boolean mIsUpgrade;
final boolean mIsPreNUpgrade;
+ final boolean mIsPreNMR1Upgrade;
/** The location for ASEC container files on internal storage. */
final String mAsecInternalPath;
@@ -2238,6 +2242,8 @@
// as there is no profiling data available.
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
+ mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
+
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
@@ -4722,20 +4728,6 @@
final ResolveInfo bestChoice =
chooseBestActivity(intent, resolvedType, flags, query, userId);
-
- if (isEphemeralAllowed(intent, query, userId)) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- final EphemeralResolveInfo ai =
- getEphemeralResolveInfo(intent, resolvedType, userId);
- if (ai != null) {
- if (DEBUG_EPHEMERAL) {
- Slog.v(TAG, "Returning an EphemeralResolveInfo");
- }
- bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
- bestChoice.ephemeralResolveInfo = ai;
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
return bestChoice;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -4776,11 +4768,23 @@
false, false, false, userId);
}
+ private boolean isEphemeralDisabled() {
+ // ephemeral apps have been disabled across the board
+ if (DISABLE_EPHEMERAL_APPS) {
+ return true;
+ }
+ // system isn't up yet; can't read settings, so, assume no ephemeral apps
+ if (!mSystemReady) {
+ return true;
+ }
+ return Secure.getInt(mContext.getContentResolver(), Secure.WEB_ACTION_ENABLED, 1) == 0;
+ }
private boolean isEphemeralAllowed(
- Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
+ Intent intent, List<ResolveInfo> resolvedActivities, int userId,
+ boolean skipPackageCheck) {
// Short circuit and return early if possible.
- if (DISABLE_EPHEMERAL_APPS) {
+ if (isEphemeralDisabled()) {
return false;
}
final int callingUser = UserHandle.getCallingUserId();
@@ -4793,18 +4797,21 @@
if (intent.getComponent() != null) {
return false;
}
- if (intent.getPackage() != null) {
+ if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
+ return false;
+ }
+ if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
final boolean isWebUri = hasWebURI(intent);
- if (!isWebUri) {
+ if (!isWebUri || intent.getData().getHost() == null) {
return false;
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
synchronized (mPackages) {
- final int count = resolvedActivites.size();
+ final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
- ResolveInfo info = resolvedActivites.get(n);
+ ResolveInfo info = resolvedActivities.get(n);
String packageName = info.activityInfo.packageName;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
@@ -4826,19 +4833,19 @@
return true;
}
- private EphemeralResolveInfo getEphemeralResolveInfo(Intent intent, String resolvedType,
- int userId) {
- final int ephemeralPrefixMask = Global.getInt(mContext.getContentResolver(),
+ private static EphemeralResolveInfo getEphemeralResolveInfo(
+ Context context, EphemeralResolverConnection resolverConnection, Intent intent,
+ String resolvedType, int userId, String packageName) {
+ final int ephemeralPrefixMask = Global.getInt(context.getContentResolver(),
Global.EPHEMERAL_HASH_PREFIX_MASK, DEFAULT_EPHEMERAL_HASH_PREFIX_MASK);
- final int ephemeralPrefixCount = Global.getInt(mContext.getContentResolver(),
+ final int ephemeralPrefixCount = Global.getInt(context.getContentResolver(),
Global.EPHEMERAL_HASH_PREFIX_COUNT, DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT);
final EphemeralDigest digest = new EphemeralDigest(intent.getData(), ephemeralPrefixMask,
ephemeralPrefixCount);
final int[] shaPrefix = digest.getDigestPrefix();
final byte[][] digestBytes = digest.getDigestBytes();
final List<EphemeralResolveInfo> ephemeralResolveInfoList =
- mEphemeralResolverConnection.getEphemeralResolveInfoList(
- shaPrefix, ephemeralPrefixMask);
+ resolverConnection.getEphemeralResolveInfoList(shaPrefix, ephemeralPrefixMask);
if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
// No hash prefix match; there are no ephemeral apps for this domain.
return null;
@@ -4855,6 +4862,10 @@
if (filters.isEmpty()) {
continue;
}
+ if (packageName != null
+ && !packageName.equals(ephemeralApplication.getPackageName())) {
+ continue;
+ }
// We have a domain match; resolve the filters to see if anything matches.
final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
for (int j = filters.size() - 1; j >= 0; --j) {
@@ -5258,8 +5269,12 @@
}
// reader
+ boolean sortResult = false;
+ boolean addEphemeral = false;
+ boolean matchEphemeralPackage = false;
+ List<ResolveInfo> result;
+ final String pkgName = intent.getPackage();
synchronized (mPackages) {
- final String pkgName = intent.getPackage();
if (pkgName == null) {
List<CrossProfileIntentFilter> matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
@@ -5267,15 +5282,16 @@
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
- List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
- result.add(xpResolveInfo);
- return filterIfNotSystemUser(result, userId);
+ List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+ xpResult.add(xpResolveInfo);
+ return filterIfNotSystemUser(xpResult, userId);
}
// Check for results in the current profile.
- List<ResolveInfo> result = mActivities.queryIntent(
- intent, resolvedType, flags, userId);
- result = filterIfNotSystemUser(result, userId);
+ result = filterIfNotSystemUser(mActivities.queryIntent(
+ intent, resolvedType, flags, userId), userId);
+ addEphemeral =
+ isEphemeralAllowed(intent, result, userId, false /*skipPackageCheck*/);
// Check for cross profile results.
boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
@@ -5287,7 +5303,7 @@
Collections.singletonList(xpResolveInfo), userId).size() > 0;
if (isVisibleToUser) {
result.add(xpResolveInfo);
- Collections.sort(result, mResolvePrioritySorter);
+ sortResult = true;
}
}
if (hasWebURI(intent)) {
@@ -5303,28 +5319,61 @@
// in the result.
result.remove(xpResolveInfo);
}
- if (result.size() == 0) {
+ if (result.size() == 0 && !addEphemeral) {
result.add(xpDomainInfo.resolveInfo);
return result;
}
- } else if (result.size() <= 1) {
- return result;
}
- result = filterCandidatesWithDomainPreferredActivitiesLPr(intent, flags, result,
- xpDomainInfo, userId);
- Collections.sort(result, mResolvePrioritySorter);
+ if (result.size() > 1 || addEphemeral) {
+ result = filterCandidatesWithDomainPreferredActivitiesLPr(
+ intent, flags, result, xpDomainInfo, userId);
+ sortResult = true;
+ }
}
- return result;
+ } else {
+ final PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ result = filterIfNotSystemUser(
+ mActivities.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.activities, userId),
+ userId);
+ } else {
+ // the caller wants to resolve for a particular package; however, there
+ // were no installed results, so, try to find an ephemeral result
+ addEphemeral = isEphemeralAllowed(
+ intent, null /*result*/, userId, true /*skipPackageCheck*/);
+ matchEphemeralPackage = true;
+ result = new ArrayList<ResolveInfo>();
+ }
}
- final PackageParser.Package pkg = mPackages.get(pkgName);
- if (pkg != null) {
- return filterIfNotSystemUser(
- mActivities.queryIntentForPackage(
- intent, resolvedType, flags, pkg.activities, userId),
- userId);
- }
- return new ArrayList<ResolveInfo>();
}
+ if (addEphemeral) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+ final EphemeralResolveInfo ai = getEphemeralResolveInfo(
+ mContext, mEphemeralResolverConnection, intent, resolvedType, userId,
+ matchEphemeralPackage ? pkgName : null);
+ if (ai != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
+ }
+ final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo);
+ ephemeralInstaller.ephemeralResolveInfo = ai;
+ // make sure this resolver is the default
+ ephemeralInstaller.isDefault = true;
+ ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
+ // add a non-generic filter
+ ephemeralInstaller.filter = new IntentFilter(intent.getAction());
+ ephemeralInstaller.filter.addDataPath(
+ intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
+ result.add(ephemeralInstaller);
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ if (sortResult) {
+ Collections.sort(result, mResolvePrioritySorter);
+ }
+ return result;
}
private static class CrossProfileDomainInfo {
@@ -6204,7 +6253,7 @@
@Override
public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
- if (DISABLE_EPHEMERAL_APPS) {
+ if (isEphemeralDisabled()) {
return null;
}
@@ -6228,7 +6277,7 @@
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isEphemeral");
- if (DISABLE_EPHEMERAL_APPS) {
+ if (isEphemeralDisabled()) {
return false;
}
@@ -6246,7 +6295,7 @@
@Override
public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
- if (DISABLE_EPHEMERAL_APPS) {
+ if (isEphemeralDisabled()) {
return null;
}
@@ -6264,7 +6313,7 @@
@Override
public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
- if (DISABLE_EPHEMERAL_APPS) {
+ if (isEphemeralDisabled()) {
return true;
}
@@ -6282,7 +6331,7 @@
@Override
public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
- if (DISABLE_EPHEMERAL_APPS) {
+ if (isEphemeralDisabled()) {
return null;
}
@@ -6612,9 +6661,13 @@
private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
final int policyFlags) throws PackageManagerException {
+ // When upgrading from pre-N MR1, verify the package time stamp using the package
+ // directory and not the APK file.
+ final long lastModifiedTime = mIsPreNMR1Upgrade
+ ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg, srcFile);
if (ps != null
&& ps.codePath.equals(srcFile)
- && ps.timeStamp == getLastModifiedTime(pkg, srcFile)
+ && ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
@@ -6636,7 +6689,7 @@
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
- Log.i(TAG, srcFile.toString() + " changed; collecting certs");
+ Slog.i(TAG, srcFile.toString() + " changed; collecting certs");
}
try {
@@ -8366,6 +8419,10 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
}
+ if (isSystemApp(pkg)) {
+ pkgSetting.isOrphaned = true;
+ }
+
ArrayList<PackageParser.Package> clientLibPkgs = null;
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
@@ -8649,7 +8706,9 @@
for (i=0; i<N; i++) {
PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
- if (cur == null) {
+ final String curPackageName = cur == null ? null : cur.info.packageName;
+ final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
+ if (cur == null || isPackageUpdate) {
mPermissionGroups.put(pg.info.name, pg);
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -8657,6 +8716,9 @@
} else {
r.append(' ');
}
+ if (isPackageUpdate) {
+ r.append("UPD:");
+ }
r.append(pg.info.name);
}
} else {
@@ -9189,15 +9251,17 @@
mEphemeralInstallerActivity.packageName = pkg.applicationInfo.packageName;
mEphemeralInstallerActivity.processName = pkg.applicationInfo.packageName;
mEphemeralInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
- ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+ mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
+ | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
mEphemeralInstallerActivity.theme = 0;
mEphemeralInstallerActivity.exported = true;
mEphemeralInstallerActivity.enabled = true;
mEphemeralInstallerInfo.activityInfo = mEphemeralInstallerActivity;
mEphemeralInstallerInfo.priority = 0;
- mEphemeralInstallerInfo.preferredOrder = 0;
- mEphemeralInstallerInfo.match = 0;
+ mEphemeralInstallerInfo.preferredOrder = 1;
+ mEphemeralInstallerInfo.isDefault = true;
+ mEphemeralInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
+ | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Set ephemeral installer activity: " + mEphemeralInstallerComponent);
@@ -15310,6 +15374,19 @@
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(observer);
final int uid = Binder.getCallingUid();
+ if (uid != Process.SHELL_UID && uid != Process.ROOT_UID && uid != Process.SYSTEM_UID
+ && uid != getPackageUid(mRequiredInstallerPackage, 0, UserHandle.getUserId(uid))
+ && !isOrphaned(packageName)
+ && !isCallerSameAsInstaller(uid, packageName)) {
+ try {
+ final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
+ intent.setData(Uri.fromParts("package", packageName, null));
+ intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
+ observer.onUserActionRequired(intent);
+ } catch (RemoteException re) {
+ }
+ return;
+ }
final boolean deleteAllUsers = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0;
final int[] users = deleteAllUsers ? sUserManager.getUserIds() : new int[]{ userId };
if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) {
@@ -15378,6 +15455,12 @@
});
}
+ private boolean isCallerSameAsInstaller(int callingUid, String pkgName) {
+ final int installerPkgUid = getPackageUid(getInstallerPackageName(pkgName),
+ 0 /* flags */, UserHandle.getUserId(callingUid));
+ return installerPkgUid == callingUid;
+ }
+
private int[] getBlockUninstallForUsers(String packageName, int[] userIds) {
int[] result = EMPTY_INT_ARRAY;
for (int userId : userIds) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 827b88a..1acc955 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -667,6 +667,12 @@
// - version code hasn't change
// - lastUpdateTime hasn't change
// - all target activities are still enabled.
+
+ // Note, system apps timestamps do *not* change after OTAs. (But they do
+ // after an adb sync or a local flash.)
+ // This means if a system app's version code doesn't change on an OTA,
+ // we don't notice it's updated. But that's fine since their version code *should*
+ // really change on OTAs.
if ((getPackageInfo().getVersionCode() == pi.versionCode)
&& (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime)
&& areAllActivitiesStillEnabled()) {
@@ -1145,6 +1151,17 @@
}
}
+ /** @return true if there's any shortcuts that are not manifest shortcuts. */
+ public boolean hasNonManifestShortcuts() {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (!si.isDeclaredInManifest()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
pw.println();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 79b5c4e..1780058 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -40,7 +40,7 @@
private final ShortcutPackageInfo mPackageInfo;
- protected final ShortcutUser mShortcutUser;
+ protected ShortcutUser mShortcutUser;
protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser,
int packageUserId, @NonNull String packageName,
@@ -51,6 +51,13 @@
mPackageInfo = Preconditions.checkNotNull(packageInfo);
}
+ /**
+ * Change the parent {@link ShortcutUser}. Need it in the restore code.
+ */
+ public void replaceUser(ShortcutUser user) {
+ mShortcutUser = user;
+ }
+
public ShortcutUser getUser() {
return mShortcutUser;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c1fc7f1..2c61f75 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -54,6 +54,7 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -324,10 +325,30 @@
int CHECK_LAUNCHER_ACTIVITY = 12;
int IS_ACTIVITY_ENABLED = 13;
int PACKAGE_UPDATE_CHECK = 14;
+ int ASYNC_PRELOAD_USER_DELAY = 15;
- int COUNT = PACKAGE_UPDATE_CHECK + 1;
+ int COUNT = ASYNC_PRELOAD_USER_DELAY + 1;
}
+ private static final String[] STAT_LABELS = {
+ "getHomeActivities()",
+ "Launcher permission check",
+ "getPackageInfo()",
+ "getPackageInfo(SIG)",
+ "getApplicationInfo",
+ "cleanupDanglingBitmaps",
+ "getActivity+metadata",
+ "getInstalledPackages",
+ "checkPackageChanges",
+ "getApplicationResources",
+ "resourceNameLookup",
+ "getLauncherActivity",
+ "checkLauncherActivity",
+ "isActivityEnabled",
+ "packageUpdateCheck",
+ "asyncPreloadUserDelay"
+ };
+
final Object mStatLock = new Object();
@GuardedBy("mStatLock")
@@ -359,6 +380,12 @@
@GuardedBy("mLock")
private Exception mLastWtfStacktrace;
+ static class InvalidFileFormatException extends Exception {
+ public InvalidFileFormatException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
public ShortcutService(Context context) {
this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false);
}
@@ -533,19 +560,26 @@
/** lifecycle event */
void handleUnlockUser(int userId) {
if (DEBUG) {
- Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mLock) {
mUnlockedUsers.put(userId, true);
-
- // Preload the user's shortcuts.
- // Also see if the locale has changed.
- // Note as of nyc, the locale is per-user, so the locale shouldn't change
- // when the user is locked. However due to b/30119489 it still happens.
- getUserShortcutsLocked(userId).detectLocaleChange();
-
- checkPackageChanges(userId);
}
+
+ // Preload the user data.
+ // Note, we don't use mHandler here but instead just start a new thread.
+ // This is because mHandler (which uses com.android.internal.os.BackgroundThread) is very
+ // busy at this point and this could take hundreds of milliseconds, which would be too
+ // late since the launcher would already have started.
+ // So we just create a new thread. This code runs rarely, so we don't use a thread pool
+ // or anything.
+ final long start = injectElapsedRealtime();
+ injectRunOnNewThread(() -> {
+ synchronized (mLock) {
+ logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
+ getUserShortcutsLocked(userId);
+ }
+ });
}
/** lifecycle event */
@@ -933,7 +967,7 @@
try {
final ShortcutUser ret = loadUserInternal(userId, in, /* forBackup= */ false);
return ret;
- } catch (IOException | XmlPullParserException e) {
+ } catch (IOException | XmlPullParserException | InvalidFileFormatException e) {
Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
return null;
} finally {
@@ -942,7 +976,8 @@
}
private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
- boolean fromBackup) throws XmlPullParserException, IOException {
+ boolean fromBackup) throws XmlPullParserException, IOException,
+ InvalidFileFormatException {
final BufferedInputStream bis = new BufferedInputStream(is);
@@ -1110,6 +1145,9 @@
userPackages = new ShortcutUser(this, userId);
}
mUsers.put(userId, userPackages);
+
+ // Also when a user's data is first accessed, scan all packages.
+ checkPackageChanges(userId);
}
return userPackages;
}
@@ -1468,6 +1506,10 @@
mHandler.post(r);
}
+ void injectRunOnNewThread(Runnable r) {
+ new Thread(r).start();
+ }
+
/**
* @throws IllegalArgumentException if {@code numShortcuts} is bigger than
* {@link #getMaxActivityShortcuts()}.
@@ -2625,10 +2667,14 @@
boolean forceRescan) {
final ShortcutUser user = getUserShortcutsLocked(userId);
+ // Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime
+ // is not reliable.
final long now = injectCurrentTimeMillis();
+ final boolean afterOta =
+ !injectBuildFingerprint().equals(user.getLastAppScanOsFingerprint());
// Then for each installed app, publish manifest shortcuts when needed.
- forUpdatedPackages(userId, lastScanTime, ai -> {
+ forUpdatedPackages(userId, lastScanTime, afterOta, ai -> {
user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId);
user.rescanPackageIfNeeded(ai.packageName, forceRescan);
});
@@ -2636,6 +2682,7 @@
// Write the time just before the scan, because there may be apps that have just
// been updated, and we want to catch them in the next time.
user.setLastAppScanTime(now);
+ user.setLastAppScanOsFingerprint(injectBuildFingerprint());
scheduleSaveUser(userId);
}
@@ -2874,7 +2921,7 @@
return parceledList.getList();
}
- private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime,
+ private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
Consumer<ApplicationInfo> callback) {
if (DEBUG) {
Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime);
@@ -2886,7 +2933,8 @@
// If the package has been updated since the last scan time, then scan it.
// Also if it's a system app with no update, lastUpdateTime is not reliable, so
// just scan it.
- if (pi.lastUpdateTime >= lastScanTime || isPureSystemApp(pi.applicationInfo)) {
+ if (pi.lastUpdateTime >= lastScanTime
+ || (afterOta && isPureSystemApp(pi.applicationInfo))) {
if (DEBUG) {
Slog.d(TAG, "Found updated package " + pi.packageName);
}
@@ -3129,15 +3177,16 @@
wtf("Can't restore: user " + userId + " is locked or not running");
return;
}
- final ShortcutUser user;
+ // Actually do restore.
+ final ShortcutUser restored;
final ByteArrayInputStream is = new ByteArrayInputStream(payload);
try {
- user = loadUserInternal(userId, is, /* fromBackup */ true);
- } catch (XmlPullParserException | IOException e) {
+ restored = loadUserInternal(userId, is, /* fromBackup */ true);
+ } catch (XmlPullParserException | IOException | InvalidFileFormatException e) {
Slog.w(TAG, "Restoration failed.", e);
return;
}
- mUsers.put(userId, user);
+ getUserShortcutsLocked(userId).mergeRestoredFile(restored);
// Rescan all packages to re-publish manifest shortcuts and do other checks.
rescanUpdatedPackagesLocked(userId,
@@ -3218,23 +3267,9 @@
pw.println(" Stats:");
synchronized (mStatLock) {
- final String p = " ";
- dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
- dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
-
- dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
- dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
- dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
- dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
- dumpStatLS(pw, p, Stats.GET_ACTIVITY_WITH_METADATA, "getActivity+metadata");
- dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages");
- dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
- dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources");
- dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup");
- dumpStatLS(pw, p, Stats.GET_LAUNCHER_ACTIVITY, "getLauncherActivity");
- dumpStatLS(pw, p, Stats.CHECK_LAUNCHER_ACTIVITY, "checkLauncherActivity");
- dumpStatLS(pw, p, Stats.IS_ACTIVITY_ENABLED, "isActivityEnabled");
- dumpStatLS(pw, p, Stats.PACKAGE_UPDATE_CHECK, "packageUpdateCheck");
+ for (int i = 0; i < Stats.COUNT; i++) {
+ dumpStatLS(pw, " ", i);
+ }
}
pw.println();
@@ -3277,12 +3312,12 @@
return tobj.format("%Y-%m-%d %H:%M:%S");
}
- private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) {
+ private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
pw.print(prefix);
final int count = mCountStats[statId];
final long dur = mDurationStats[statId];
pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
- label, count, dur,
+ STAT_LABELS[statId], count, dur,
(count == 0 ? 0 : ((double) dur) / count)));
}
@@ -3578,6 +3613,12 @@
Binder.restoreCallingIdentity(token);
}
+ // Injection point.
+ @VisibleForTesting
+ String injectBuildFingerprint() {
+ return Build.FINGERPRINT;
+ }
+
final void wtf(String message) {
wtf(message, /* exception= */ null);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index ce3ed9c..5d4bfa4 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -23,12 +23,14 @@
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.ShortcutService.InvalidFileFormatException;
import libcore.util.Objects;
@@ -60,6 +62,7 @@
// Suffix "2" was added to force rescan all packages after the next OTA.
private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time2";
+ private static final String ATTR_LAST_APP_SCAN_OS_FINGERPRINT = "last-app-scan-fp";
private static final String KEY_USER_ID = "userId";
private static final String KEY_LAUNCHERS = "launchers";
private static final String KEY_PACKAGES = "packages";
@@ -125,6 +128,8 @@
private long mLastAppScanTime;
+ private String mLastAppScanOsFingerprint;
+
public ShortcutUser(ShortcutService service, int userId) {
mService = service;
mUserId = userId;
@@ -142,6 +147,14 @@
mLastAppScanTime = lastAppScanTime;
}
+ public String getLastAppScanOsFingerprint() {
+ return mLastAppScanOsFingerprint;
+ }
+
+ public void setLastAppScanOsFingerprint(String lastAppScanOsFingerprint) {
+ mLastAppScanOsFingerprint = lastAppScanOsFingerprint;
+ }
+
// We don't expose this directly to non-test code because only ShortcutUser should add to/
// remove from it.
@VisibleForTesting
@@ -153,6 +166,11 @@
return mPackages.containsKey(packageName);
}
+ private void addPackage(@NonNull ShortcutPackage p) {
+ p.replaceUser(this);
+ mPackages.put(p.getPackageName(), p);
+ }
+
public ShortcutPackage removePackage(@NonNull String packageName) {
final ShortcutPackage removed = mPackages.remove(packageName);
@@ -168,7 +186,8 @@
return mLaunchers;
}
- public void addLauncher(ShortcutLauncher launcher) {
+ private void addLauncher(ShortcutLauncher launcher) {
+ launcher.replaceUser(this);
mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(),
launcher.getPackageName()), launcher);
}
@@ -315,11 +334,16 @@
throws IOException, XmlPullParserException {
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales);
- ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME,
- mLastAppScanTime);
+ if (!forBackup) {
+ // Don't have to back them up.
+ ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALES, mKnownLocales);
+ ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME,
+ mLastAppScanTime);
+ ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_OS_FINGERPRINT,
+ mLastAppScanOsFingerprint);
- ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher);
+ ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLastKnownLauncher);
+ }
// Can't use forEachPackageItem due to the checked exceptions.
{
@@ -352,53 +376,59 @@
}
public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
- boolean fromBackup) throws IOException, XmlPullParserException {
+ boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
final ShortcutUser ret = new ShortcutUser(s, userId);
- ret.mKnownLocales = ShortcutService.parseStringAttribute(parser,
- ATTR_KNOWN_LOCALES);
+ try {
+ ret.mKnownLocales = ShortcutService.parseStringAttribute(parser,
+ ATTR_KNOWN_LOCALES);
- // If lastAppScanTime is in the future, that means the clock went backwards.
- // Just scan all apps again.
- final long lastAppScanTime = ShortcutService.parseLongAttribute(parser,
- ATTR_LAST_APP_SCAN_TIME);
- final long currentTime = s.injectCurrentTimeMillis();
- ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0;
+ // If lastAppScanTime is in the future, that means the clock went backwards.
+ // Just scan all apps again.
+ final long lastAppScanTime = ShortcutService.parseLongAttribute(parser,
+ ATTR_LAST_APP_SCAN_TIME);
+ final long currentTime = s.injectCurrentTimeMillis();
+ ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0;
+ ret.mLastAppScanOsFingerprint = ShortcutService.parseStringAttribute(parser,
+ ATTR_LAST_APP_SCAN_OS_FINGERPRINT);
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final int depth = parser.getDepth();
- final String tag = parser.getName();
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case TAG_LAUNCHER: {
+ ret.mLastKnownLauncher = ShortcutService.parseComponentNameAttribute(
+ parser, ATTR_VALUE);
+ continue;
+ }
+ case ShortcutPackage.TAG_ROOT: {
+ final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
+ s, ret, parser, fromBackup);
- if (depth == outerDepth + 1) {
- switch (tag) {
- case TAG_LAUNCHER: {
- ret.mLastKnownLauncher = ShortcutService.parseComponentNameAttribute(
- parser, ATTR_VALUE);
- continue;
- }
- case ShortcutPackage.TAG_ROOT: {
- final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
- s, ret, parser, fromBackup);
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
+ continue;
+ }
- // Don't use addShortcut(), we don't need to save the icon.
- ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
- continue;
- }
-
- case ShortcutLauncher.TAG_ROOT: {
- ret.addLauncher(
- ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
- continue;
+ case ShortcutLauncher.TAG_ROOT: {
+ ret.addLauncher(
+ ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
+ continue;
+ }
}
}
+ ShortcutService.warnForInvalidTag(depth, tag);
}
- ShortcutService.warnForInvalidTag(depth, tag);
+ } catch (RuntimeException e) {
+ throw new ShortcutService.InvalidFileFormatException(
+ "Unable to parse file", e);
}
return ret;
}
@@ -447,6 +477,51 @@
}
}
+ public void mergeRestoredFile(ShortcutUser restored) {
+ final ShortcutService s = mService;
+ // Note, a restore happens only at the end of setup wizard. At this point, no apps are
+ // installed from Play Store yet, but it's still possible that system apps have already
+ // published dynamic shortcuts, since some apps do so on BOOT_COMPLETED.
+ // When such a system app has allowbackup=true, then we go ahead and replace all existing
+ // shortcuts with the restored shortcuts. (Then we'll re-publish manifest shortcuts later
+ // in the call site.)
+ // When such a system app has allowbackup=false, then we'll keep the shortcuts that have
+ // already been published. So we selectively add restored ShortcutPackages here.
+ //
+ // The same logic applies to launchers, but since launchers shouldn't pin shortcuts
+ // without users interaction it's really not a big deal, so we just clear existing
+ // ShortcutLauncher instances in mLaunchers and add all the restored ones here.
+
+ mLaunchers.clear();
+ restored.forAllLaunchers(sl -> {
+ // If the app is already installed and allowbackup = false, then ignore the restored
+ // data.
+ if (s.isPackageInstalled(sl.getPackageName(), getUserId())
+ && !s.shouldBackupApp(sl.getPackageName(), getUserId())) {
+ return;
+ }
+ addLauncher(sl);
+ });
+ restored.forAllPackages(sp -> {
+ // If the app is already installed and allowbackup = false, then ignore the restored
+ // data.
+ if (s.isPackageInstalled(sp.getPackageName(), getUserId())
+ && !s.shouldBackupApp(sp.getPackageName(), getUserId())) {
+ return;
+ }
+
+ final ShortcutPackage previous = getPackageShortcutsIfExists(sp.getPackageName());
+ if (previous != null && previous.hasNonManifestShortcuts()) {
+ Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ + " Existing non-manifeset shortcuts will be overwritten.");
+ }
+ addPackage(sp);
+ });
+ // Empty the launchers and packages in restored to avoid accidentally using them.
+ restored.mLaunchers.clear();
+ restored.mPackages.clear();
+ }
+
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
pw.print(prefix);
pw.print("User: ");
@@ -457,6 +532,8 @@
pw.print(mLastAppScanTime);
pw.print("] ");
pw.print(ShortcutService.formatTime(mLastAppScanTime));
+ pw.print(" Last app scan FP: ");
+ pw.print(mLastAppScanOsFingerprint);
pw.println();
prefix += prefix + " ";
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c9ad49a..af055da 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -180,7 +180,8 @@
UserInfo.FLAG_MANAGED_PROFILE
| UserInfo.FLAG_EPHEMERAL
| UserInfo.FLAG_RESTRICTED
- | UserInfo.FLAG_GUEST;
+ | UserInfo.FLAG_GUEST
+ | UserInfo.FLAG_DEMO;
private static final int MIN_USER_ID = 10;
// We need to keep process uid within Integer.MAX_VALUE.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a39add8..1132ff9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5318,15 +5318,18 @@
boolean showing = mKeyguardDelegate.isShowing();
if (wasOccluded && !isOccluded && showing) {
mKeyguardOccluded = false;
- mKeyguardDelegate.setOccluded(false);
+ mKeyguardDelegate.setOccluded(false, true /* animate */);
mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
}
+ Animation anim = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.wallpaper_open_exit);
+ mWindowManagerFuncs.overridePlayingAppAnimationsLw(anim);
return true;
} else if (!wasOccluded && isOccluded && showing) {
mKeyguardOccluded = true;
- mKeyguardDelegate.setOccluded(true);
+ mKeyguardDelegate.setOccluded(true, false /* animate */);
mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
return true;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 4fce49e..2af4c9b 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -180,7 +180,7 @@
mKeyguardService.onBootCompleted();
}
if (mKeyguardState.occluded) {
- mKeyguardService.setOccluded(mKeyguardState.occluded);
+ mKeyguardService.setOccluded(mKeyguardState.occluded, false /* animate */);
}
}
@@ -232,10 +232,10 @@
}
}
- public void setOccluded(boolean isOccluded) {
+ public void setOccluded(boolean isOccluded, boolean animate) {
if (mKeyguardService != null) {
- if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
- mKeyguardService.setOccluded(isOccluded);
+ if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
+ mKeyguardService.setOccluded(isOccluded, animate);
}
mKeyguardState.occluded = isOccluded;
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 55652fe..2169927 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -63,9 +63,9 @@
}
@Override // Binder interface
- public void setOccluded(boolean isOccluded) {
+ public void setOccluded(boolean isOccluded, boolean animate) {
try {
- mService.setOccluded(isOccluded);
+ mService.setOccluded(isOccluded, animate);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d9c4254..a7b9cf4 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -102,9 +102,8 @@
private static final int MSG_START_USER = 7;
private static final int MSG_CLEANUP_USER = 8;
private static final int MSG_SWITCH_USER = 9;
- private static final int MSG_SET_DEVICE_LOCKED = 10;
- private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 11;
- private static final int MSG_UNLOCK_USER = 12;
+ private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 10;
+ private static final int MSG_UNLOCK_USER = 11;
private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
@@ -317,20 +316,6 @@
}
}
- public void setDeviceLockedForUser(int userId, boolean locked) {
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
- synchronized (mDeviceLockedForUser) {
- mDeviceLockedForUser.put(userId, locked);
- }
- if (locked) {
- try {
- ActivityManagerNative.getDefault().notifyLockedProfile(userId);
- } catch (RemoteException e) {
- }
- }
- }
- }
-
boolean isDeviceLockedInner(int userId) {
synchronized (mDeviceLockedForUser) {
return mDeviceLockedForUser.get(userId, true);
@@ -838,10 +823,24 @@
}
@Override
- public void setDeviceLockedForUser(int userId, boolean value) {
+ public void setDeviceLockedForUser(int userId, boolean locked) {
enforceReportPermission();
- mHandler.obtainMessage(MSG_SET_DEVICE_LOCKED, value ? 1 : 0, userId)
- .sendToTarget();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ synchronized (mDeviceLockedForUser) {
+ mDeviceLockedForUser.put(userId, locked);
+ }
+ if (locked) {
+ try {
+ ActivityManagerNative.getDefault().notifyLockedProfile(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
@@ -917,9 +916,6 @@
mCurrentUser = msg.arg1;
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
- case MSG_SET_DEVICE_LOCKED:
- setDeviceLockedForUser(msg.arg2, msg.arg1 != 0);
- break;
case MSG_FLUSH_TRUST_USUALLY_MANAGED:
SparseBooleanArray usuallyManaged;
synchronized (mTrustUsuallyManagedForUser) {
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index acd6587..db7df25 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -151,7 +151,7 @@
}
private void startListening() {
- if (DEBUG) Slog.d(TAG, "startListening");
+ Slog.d(TAG, "startListening");
// Start listening for location updates (default: low power, max 1h, min 10m).
mLocationManager.requestLocationUpdates(
@@ -173,7 +173,7 @@
mTimeChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (DEBUG) Slog.d(TAG, "onReceive: " + intent);
+ Slog.d(TAG, "onReceive: " + intent);
updateTwilightState();
}
};
@@ -188,7 +188,7 @@
}
private void stopListening() {
- if (DEBUG) Slog.d(TAG, "stopListening");
+ Slog.d(TAG, "stopListening");
if (mTimeChangedReceiver != null) {
getContext().unregisterReceiver(mTimeChangedReceiver);
@@ -241,15 +241,20 @@
@Override
public void onAlarm() {
- if (DEBUG) Slog.d(TAG, "onAlarm");
+ Slog.d(TAG, "onAlarm");
updateTwilightState();
}
@Override
public void onLocationChanged(Location location) {
- if (DEBUG) Slog.d(TAG, "onLocationChanged: " + location);
- mLastLocation = location;
- updateTwilightState();
+ if (location != null) {
+ Slog.d(TAG, "onLocationChanged:"
+ + " provider=" + location.getProvider()
+ + " accuracy=" + location.getAccuracy()
+ + " time=" + location.getTime());
+ mLastLocation = location;
+ updateTwilightState();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d4d6f32..e4ec295 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -382,7 +382,7 @@
topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null,
topClosingAppAnimator != null ? topClosingAppAnimator.animation : null);
mService.getDefaultDisplayContentLocked().getDockedDividerController()
- .notifyAppTransitionStarting();
+ .notifyAppTransitionStarting(openingApps);
// Prolong the start for the transition when docking a task from recents, unless recents
// ended it already then we don't need to wait.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 621e43a..a8a0b0e 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -30,6 +30,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.WINDOW_REPLACEMENT_TIMEOUT_DURATION;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import com.android.server.input.InputApplicationHandle;
import com.android.server.wm.WindowManagerService.H;
@@ -44,6 +45,7 @@
import android.view.IApplicationToken;
import android.view.View;
import android.view.WindowManager;
+import android.view.animation.Animation;
import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -838,6 +840,18 @@
}
}
+ /**
+ * See {@link WindowManagerService#overridePlayingAppAnimationsLw}
+ */
+ void overridePlayingAppAnimations(Animation a) {
+ if (mAppAnimator.isAnimating()) {
+ final WindowState win = findMainWindow();
+ final int width = win.mContainingFrame.width();
+ final int height = win.mContainingFrame.height();
+ mAppAnimator.setAnimation(a, width, height, false, STACK_CLIP_NONE);
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1d57872..9b5b101 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -35,6 +35,7 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.animation.Animation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -637,7 +638,7 @@
*/
TaskStack getDockedStackVisibleForUserLocked() {
final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
- return (stack != null && stack.isVisibleForUserLocked()) ? stack : null;
+ return (stack != null && stack.isVisibleLocked(true /* ignoreKeyguard */)) ? stack : null;
}
/**
@@ -674,4 +675,13 @@
return touchedWin;
}
+
+ /**
+ * See {@link WindowManagerService#overridePlayingAppAnimationsLw}.
+ */
+ void overridePlayingAppAnimationsLw(Animation a) {
+ for (int i = mStacks.size() - 1; i >= 0; i--) {
+ mStacks.get(i).overridePlayingAppAnimations(a);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index f93e2ff..f8d2ee9 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -38,6 +38,7 @@
import android.graphics.Rect;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDockedStackListener;
@@ -492,8 +493,31 @@
checkMinimizeChanged(false /* animate */);
}
- void notifyAppTransitionStarting() {
+ void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps) {
+ final boolean wasMinimized = mMinimizedDock;
checkMinimizeChanged(true /* animate */);
+
+ // We were minimized, and now we are still minimized, but somebody is trying to launch an
+ // app in docked stack, better show recent apps so we actually get unminimized! This catches
+ // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because
+ // we couldn't retrace the launch of the app in the docked stack to the launch from
+ // homescreen.
+ if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)) {
+ mService.showRecentApps(true /* fromHome */);
+ }
+ }
+
+ /**
+ * @return true if {@param apps} contains an activity in the docked stack, false otherwise.
+ */
+ private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ final AppWindowToken token = apps.valueAt(i);
+ if (token.mTask != null && token.mTask.mStack.mStackId == DOCKED_STACK_ID) {
+ return true;
+ }
+ }
+ return false;
}
boolean isMinimizedDock() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1dcada6..ca183010 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -38,6 +38,7 @@
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.animation.Animation;
import com.android.server.EventLogTags;
@@ -677,19 +678,6 @@
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
- boolean isVisibleForUser() {
- for (int i = mAppTokens.size() - 1; i >= 0; i--) {
- final AppWindowToken appToken = mAppTokens.get(i);
- for (int j = appToken.allAppWindows.size() - 1; j >= 0; j--) {
- WindowState window = appToken.allAppWindows.get(j);
- if (!window.isHiddenFromUserLocked()) {
- return true;
- }
- }
- }
- return false;
- }
-
boolean isVisible() {
for (int i = mAppTokens.size() - 1; i >= 0; i--) {
final AppWindowToken appToken = mAppTokens.get(i);
@@ -778,6 +766,15 @@
return mStack.getDisplayContent().getDisplayInfo();
}
+ /**
+ * See {@link WindowManagerService#overridePlayingAppAnimationsLw}
+ */
+ void overridePlayingAppAnimations(Animation a) {
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ mAppTokens.get(i).overridePlayingAppAnimations(a);
+ }
+ }
+
@Override
public String toString() {
return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8be5b19..8f8f642 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -44,6 +44,7 @@
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.animation.Animation;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -398,23 +399,21 @@
return false;
}
- final int oldDockSide = mStackId == DOCKED_STACK_ID ? getDockSide() : DOCKED_INVALID;
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (mStackId == DOCKED_STACK_ID) {
repositionDockedStackAfterRotation(mTmpRect2);
snapDockedStackAfterRotation(mTmpRect2);
final int newDockSide = getDockSide(mTmpRect2);
- if (oldDockSide != newDockSide) {
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
- }
+
+ // Update the dock create mode and clear the dock create bounds, these
+ // might change after a rotation and the original values will be invalid.
+ mService.setDockedStackCreateStateLocked(
+ (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+ ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ null);
+ mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
}
mBoundsAfterRotation.set(mTmpRect2);
@@ -890,7 +889,7 @@
mAdjustImeAmount = adjustAmount;
mAdjustDividerAmount = adjustDividerAmount;
updateAdjustedBounds();
- return isVisibleForUserLocked();
+ return isVisibleLocked(true /* ignoreKeyguard */);
} else {
return false;
}
@@ -926,7 +925,7 @@
if (minimizeAmount != mMinimizeAmount) {
mMinimizeAmount = minimizeAmount;
updateAdjustedBounds();
- return isVisibleForUserLocked();
+ return isVisibleLocked(true /* ignoreKeyguard*/);
} else {
return false;
}
@@ -943,7 +942,7 @@
void beginImeAdjustAnimation() {
for (int j = mTasks.size() - 1; j >= 0; j--) {
final Task task = mTasks.get(j);
- if (task.isVisibleForUser()) {
+ if (task.isVisible()) {
task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
task.addWindowsWaitingForDrawnIfResizingChanged();
}
@@ -1233,9 +1232,13 @@
}
boolean isVisibleLocked() {
+ return isVisibleLocked(false /* ignoreKeyguard */);
+ }
+
+ boolean isVisibleLocked(boolean ignoreKeyguard) {
final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded()
&& !mService.mAnimator.mKeyguardGoingAway;
- if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
+ if (!ignoreKeyguard && keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
// The keyguard is showing and the stack shouldn't show on top of the keyguard.
return false;
}
@@ -1252,20 +1255,6 @@
return false;
}
- /**
- * @return true if a the stack is visible for the current in user, ignoring any other visibility
- * aspects, and false otherwise
- */
- boolean isVisibleForUserLocked() {
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- final Task task = mTasks.get(i);
- if (task.isVisibleForUser()) {
- return true;
- }
- }
- return false;
- }
-
boolean isDragResizing() {
return mDragResizing;
}
@@ -1379,4 +1368,13 @@
public boolean getBoundsAnimating() {
return mBoundsAnimating;
}
+
+ /**
+ * See {@link WindowManagerService#overridePlayingAppAnimationsLw}
+ */
+ void overridePlayingAppAnimations(Animation a) {
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ mTasks.get(i).overridePlayingAppAnimations(a);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 2b66c3a..e7ceba9 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -757,14 +757,16 @@
}
// Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
- // layer. For keyguard over wallpaper put the wallpaper under the keyguard.
+ // layer. For keyguard over wallpaper put the wallpaper under the lowest window that
+ // is currently on screen, i.e. not hidden by policy.
int insertionIndex = 0;
if (visible && wallpaperTarget != null) {
final int type = wallpaperTarget.mAttrs.type;
final int privateFlags = wallpaperTarget.mAttrs.privateFlags;
if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0
|| type == TYPE_KEYGUARD_SCRIM) {
- insertionIndex = windows.indexOf(wallpaperTarget);
+ insertionIndex = Math.min(windows.indexOf(wallpaperTarget),
+ findLowestWindowOnScreen(windows));
}
}
if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
@@ -781,6 +783,21 @@
return changed;
}
+ /**
+ * @return The index in {@param windows} of the lowest window that is currently on screen and
+ * not hidden by the policy.
+ */
+ private int findLowestWindowOnScreen(WindowList windows) {
+ final int size = windows.size();
+ for (int index = 0; index < size; index++) {
+ final WindowState win = windows.get(index);
+ if (win.isOnScreen()) {
+ return index;
+ }
+ }
+ return Integer.MAX_VALUE;
+ }
+
boolean adjustWallpaperWindows() {
mService.mWindowPlacerLocked.mWallpaperMayChange = false;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b0d357c..47b0f3b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -234,14 +234,12 @@
boolean allowWhenLocked = false;
// Show IME over the keyguard if the target allows it
allowWhenLocked |= (win.mIsImWindow || imeTarget == win) && showImeOverKeyguard;
- // Show SHOW_WHEN_LOCKED windows that turn on the screen
- allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.mTurnOnScreen;
+ // Show SHOW_WHEN_LOCKED windows
+ allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
if (appShowWhenLocked != null) {
allowWhenLocked |= appShowWhenLocked == win.mAppToken
- // Show all SHOW_WHEN_LOCKED windows if some apps are shown over lockscreen
- || (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
- // Show error dialogs over apps that dismiss keyguard.
+ // Show error dialogs over apps that are shown on lockscreen
|| (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eb9ad6c..47a4114 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -249,6 +249,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -2908,12 +2909,11 @@
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
- final WindowSurfaceController surfaceController = winAnimator.mSurfaceController;
- if (viewVisibility == View.VISIBLE && surfaceController != null) {
+ if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
// We already told the client to go invisible, but the message may not be
// handled yet, or it might want to draw a last frame. If we already have a
// surface, let the client use that, but don't create new surface at this point.
- surfaceController.getSurface(outSurface);
+ winAnimator.mSurfaceController.getSurface(outSurface);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -5212,6 +5212,11 @@
}
}
+ @Override
+ public void overridePlayingAppAnimationsLw(Animation a) {
+ getDefaultDisplayContentLocked().overridePlayingAppAnimationsLw(a);
+ }
+
/**
* Re-sizes a stack and its containing tasks.
* @param stackId Id of stack to resize.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e828650..a96cef0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3673,12 +3673,16 @@
private boolean isActivePasswordSufficientForUserLocked(
DevicePolicyData policy, int userHandle, boolean parent) {
- if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle, parent)
- || policy.mActivePasswordLength < getPasswordMinimumLength(
+ final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
+ if (policy.mActivePasswordQuality < requiredPasswordQuality) {
+ return false;
+ }
+ if (requiredPasswordQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ && policy.mActivePasswordLength < getPasswordMinimumLength(
null, userHandle, parent)) {
return false;
}
- if (policy.mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+ if (requiredPasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
return true;
}
return policy.mActivePasswordUpperCase >= getPasswordMinimumUpperCase(
diff --git a/services/net/Android.mk b/services/net/Android.mk
index 336bc45..408794e 100644
--- a/services/net/Android.mk
+++ b/services/net/Android.mk
@@ -7,4 +7,7 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_AIDL_INCLUDES += \
+ system/netd/server/binder
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 654ef18..6d90203 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -382,6 +382,7 @@
private final State mStoppedState = new StoppedState();
private final State mStoppingState = new StoppingState();
private final State mStartedState = new StartedState();
+ private final State mRunningState = new RunningState();
private final String mTag;
private final Context mContext;
@@ -476,6 +477,7 @@
// Super simple StateMachine.
addState(mStoppedState);
addState(mStartedState);
+ addState(mRunningState, mStartedState);
addState(mStoppingState);
setInitialState(mStoppedState);
@@ -570,7 +572,7 @@
pw.decreaseIndent();
pw.println();
- pw.println("StateMachine dump:");
+ pw.println(mTag + " StateMachine dump:");
pw.increaseIndent();
mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
pw.decreaseIndent();
@@ -768,6 +770,11 @@
// - IPv6 addresses
// - IPv6 routes
// - IPv6 DNS servers
+ //
+ // N.B.: this is fundamentally race-prone and should be fixed by
+ // changing NetlinkTracker from a hybrid edge/level model to an
+ // edge-only model, or by giving IpManager its own netlink socket(s)
+ // so as to track all required information directly.
LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
@@ -939,16 +946,30 @@
return true;
}
+ private void stopAllIP() {
+ // We don't need to worry about routes, just addresses, because:
+ // - disableIpv6() will clear autoconf IPv6 routes as well, and
+ // - we don't get IPv4 routes from netlink
+ // so we neither react to nor need to wait for changes in either.
+
+ try {
+ mNwService.disableIpv6(mInterfaceName);
+ } catch (Exception e) {
+ Log.e(mTag, "Failed to disable IPv6" + e);
+ }
+
+ try {
+ mNwService.clearInterfaceAddresses(mInterfaceName);
+ } catch (Exception e) {
+ Log.e(mTag, "Failed to clear addresses " + e);
+ }
+ }
+
class StoppedState extends State {
@Override
public void enter() {
- try {
- mNwService.disableIpv6(mInterfaceName);
- mNwService.clearInterfaceAddresses(mInterfaceName);
- } catch (Exception e) {
- Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
- }
+ stopAllIP();
resetLinkProperties();
if (mStartTimeMillis > 0) {
@@ -1023,12 +1044,71 @@
}
class StartedState extends State {
- private boolean mDhcpActionInFlight;
-
@Override
public void enter() {
mStartTimeMillis = SystemClock.elapsedRealtime();
+ if (mConfiguration.mProvisioningTimeoutMs > 0) {
+ final long alarmTime = SystemClock.elapsedRealtime() +
+ mConfiguration.mProvisioningTimeoutMs;
+ mProvisioningTimeoutAlarm.schedule(alarmTime);
+ }
+
+ if (readyToProceed()) {
+ transitionTo(mRunningState);
+ } else {
+ // Clear all IPv4 and IPv6 before proceeding to RunningState.
+ // Clean up any leftover state from an abnormal exit from
+ // tethering or during an IpManager restart.
+ stopAllIP();
+ }
+ }
+
+ @Override
+ public void exit() {
+ mProvisioningTimeoutAlarm.cancel();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_STOP:
+ transitionTo(mStoppingState);
+ break;
+
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ if (readyToProceed()) {
+ transitionTo(mRunningState);
+ }
+ break;
+
+ case EVENT_PROVISIONING_TIMEOUT:
+ handleProvisioningFailure();
+ break;
+
+ default:
+ // It's safe to process messages out of order because the
+ // only message that can both
+ // a) be received at this time and
+ // b) affect provisioning state
+ // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
+ deferMessage(msg);
+ }
+ return HANDLED;
+ }
+
+ boolean readyToProceed() {
+ return (!mLinkProperties.hasIPv4Address() &&
+ !mLinkProperties.hasGlobalIPv6Address());
+ }
+ }
+
+ class RunningState extends State {
+ private boolean mDhcpActionInFlight;
+
+ @Override
+ public void enter() {
mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
mCallback, mMulticastFiltering);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
@@ -1037,12 +1117,6 @@
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime() +
- mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
-
if (mConfiguration.mEnableIPv6) {
// TODO: Consider transitionTo(mStoppingState) if this fails.
startIPv6();
@@ -1070,7 +1144,6 @@
@Override
public void exit() {
- mProvisioningTimeoutAlarm.cancel();
stopDhcpAction();
if (mIpReachabilityMonitor != null) {
@@ -1167,10 +1240,6 @@
break;
}
- case EVENT_PROVISIONING_TIMEOUT:
- handleProvisioningFailure();
- break;
-
case EVENT_DHCPACTION_TIMEOUT:
stopDhcpAction();
break;
diff --git a/services/net/java/android/net/util/NetdService.java b/services/net/java/android/net/util/NetdService.java
new file mode 100644
index 0000000..153cb50
--- /dev/null
+++ b/services/net/java/android/net/util/NetdService.java
@@ -0,0 +1,46 @@
+/*
+ * 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.net.util;
+
+import android.net.INetd;
+import android.os.ServiceManager;
+import android.util.Log;
+
+
+/**
+ * @hide
+ */
+public class NetdService {
+ private static final String TAG = NetdService.class.getSimpleName();
+ private static final String NETD_SERVICE_NAME = "netd";
+
+ /**
+ * It is the caller's responsibility to check for a null return value
+ * and to handle RemoteException errors from invocations on the returned
+ * interface if, for example, netd dies and is restarted.
+ *
+ * @return an INetd instance or null.
+ */
+ public static INetd getInstance() {
+ final INetd netdInstance = INetd.Stub.asInterface(
+ ServiceManager.getService(NETD_SERVICE_NAME));
+ if (netdInstance == null) {
+ Log.w(TAG, "WARNING: returning null INetd instance.");
+ }
+ return netdInstance;
+ }
+}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index 07cc9c0..07b26e8 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -57,6 +57,9 @@
* spooler if needed, to make the timed remote calls, to handle
* remote exceptions, and to bind/unbind to the remote instance as
* needed.
+ *
+ * The calls might be blocking and need the main thread of to be unblocked to finish. Hence do not
+ * call this while holding any monitors that might need to be acquired the main thread.
*/
final class RemotePrintSpooler {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 05301c1..a91cdb3 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -434,12 +434,12 @@
}
public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
+ mSpooler.clearCustomPrinterIconCache();
+
synchronized (mLock) {
throwIfDestroyedLocked();
if (mPrinterDiscoverySession == null) {
- mSpooler.clearCustomPrinterIconCache();
-
// If we do not have a session, tell all service to create one.
mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) {
@Override
@@ -731,6 +731,8 @@
@Override
public void onCustomPrinterIconLoaded(PrinterId printerId, Icon icon) {
+ mSpooler.onCustomPrinterIconLoaded(printerId, icon);
+
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -738,7 +740,6 @@
if (mPrinterDiscoverySession == null) {
return;
}
- mSpooler.onCustomPrinterIconLoaded(printerId, icon);
mPrinterDiscoverySession.onCustomPrinterIconLoadedLocked(printerId);
}
}
@@ -979,18 +980,21 @@
* Prune persistent state if a print service was uninstalled
*/
public void prunePrintServices() {
+ ArrayList<ComponentName> installedComponents;
+
synchronized (mLock) {
- ArrayList<ComponentName> installedComponents = getInstalledComponents();
+ installedComponents = getInstalledComponents();
// Remove unnecessary entries from persistent state "disabled services"
boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents);
if (disabledServicesUninstalled) {
writeDisabledPrintServicesLocked(mDisabledServices);
}
-
- // Remove unnecessary entries from persistent state "approved services"
- mSpooler.pruneApprovedPrintServices(installedComponents);
}
+
+ // Remove unnecessary entries from persistent state "approved services"
+ mSpooler.pruneApprovedPrintServices(installedComponents);
+
}
private void onConfigurationChangedLocked() {
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 404c142..a3d0afa 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.when;
import android.accounts.Account;
+import android.accounts.AccountManagerInternal;
import android.accounts.AuthenticatorDescription;
import android.app.AppOpsManager;
import android.app.Notification;
@@ -44,6 +45,8 @@
import android.test.mock.MockPackageManager;
import android.util.Log;
+import com.android.server.LocalServices;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -73,6 +76,7 @@
SQLiteDatabase.deleteDatabase(new File(mAms.getCeDatabaseName(UserHandle.USER_SYSTEM)));
SQLiteDatabase.deleteDatabase(new File(mAms.getDeDatabaseName(UserHandle.USER_SYSTEM)));
SQLiteDatabase.deleteDatabase(new File(mAms.getPreNDatabaseName(UserHandle.USER_SYSTEM)));
+ LocalServices.removeServiceForTest(AccountManagerInternal.class);
super.tearDown();
}
@@ -282,6 +286,7 @@
private AccountManagerService createAccountManagerService(Context mockContext,
Context realContext) {
+ LocalServices.removeServiceForTest(AccountManagerInternal.class);
return new MyAccountManagerService(mockContext,
new MyMockPackageManager(), new MockAccountAuthenticatorCache(), realContext);
}
@@ -420,6 +425,11 @@
public int checkSignatures(final int uid1, final int uid2) {
return PackageManager.SIGNATURE_MATCH;
}
+
+ @Override
+ public void addOnPermissionsChangeListener(
+ OnPermissionsChangedListener listener) {
+ }
}
static public class MyAccountManagerService extends AccountManagerService {
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index e440a0d..984a484 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -62,7 +62,7 @@
for (int i = 0; i < 100; i++) {
taskIdsOnFile.put(getRandomTaskIdForUser(testUserId), true);
}
- mTaskPersister.maybeWritePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
+ mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
SparseBooleanArray newTaskIdsOnFile = mTaskPersister
.loadPersistedTaskIdsForUser(testUserId);
assertTrue("TaskIds written differ from TaskIds read back from file",
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 1c7a138..792f300 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -390,6 +390,11 @@
}
@Override
+ void injectRunOnNewThread(Runnable r) {
+ runOnHandler(r);
+ }
+
+ @Override
void injectEnforceCallingPermission(String permission, String message) {
if (!mCallerPermissions.contains(permission)) {
throw new SecurityException("Missing permission: " + permission);
@@ -402,6 +407,11 @@
}
@Override
+ String injectBuildFingerprint() {
+ return mInjectedBuildFingerprint;
+ }
+
+ @Override
void wtf(String message, Throwable th) {
// During tests, WTF is fatal.
fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th));
@@ -523,6 +533,7 @@
protected Map<String, PackageInfo> mInjectedPackages;
protected Set<PackageWithUser> mUninstalledPackages;
+ protected Set<String> mSystemPackages;
protected PackageManager mMockPackageManager;
protected PackageManagerInternal mMockPackageManagerInternal;
@@ -623,6 +634,8 @@
protected static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback";
protected static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999;
+ protected String mInjectedBuildFingerprint = "build1";
+
static {
QUERY_ALL.setQueryFlags(
ShortcutQuery.FLAG_GET_ALL_KINDS);
@@ -672,6 +685,7 @@
pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
mUninstalledPackages = new HashSet<>();
+ mSystemPackages = new HashSet<>();
mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
@@ -921,6 +935,12 @@
});
}
+ protected void setPackageLastUpdateTime(String packageName, long value) {
+ updatePackageInfo(packageName, pi -> {
+ pi.lastUpdateTime = value;
+ });
+ }
+
protected void uninstallPackage(int userId, String packageName) {
if (ENABLE_DUMP) {
Log.v(TAG, "Unnstall package " + packageName + " / " + userId);
@@ -952,6 +972,9 @@
if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) {
ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
}
+ if (mSystemPackages.contains(packageName)) {
+ ret.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
if (getSignatures) {
ret.signatures = pi.signatures;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 253334e..143398f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -3945,11 +3945,11 @@
mInjectedPackages.remove(CALLING_PACKAGE_1);
mInjectedPackages.remove(CALLING_PACKAGE_3);
- mService.handleUnlockUser(USER_0);
+ mService.checkPackageChanges(USER_0);
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
- assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); // ---------------
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
@@ -3961,7 +3961,7 @@
assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
- mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
@@ -4154,7 +4154,7 @@
updatePackageVersion(CALLING_PACKAGE_1, 1);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
waitOnMainThread();
@@ -4186,10 +4186,13 @@
mInjectedCurrentTimeMillis = START_TIME + 200;
mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
reset(c0);
reset(c10);
+ setPackageLastUpdateTime(CALLING_PACKAGE_1, mInjectedCurrentTimeMillis);
mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
waitOnMainThread();
@@ -4221,7 +4224,7 @@
// Then send the broadcast, to only user-0.
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_2, USER_0));
- mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
waitOnMainThread();
@@ -4243,9 +4246,9 @@
updatePackageVersion(CALLING_PACKAGE_3, 100);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_3, USER_0));
- mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
waitOnMainThread();
@@ -4344,6 +4347,128 @@
});
}
+ public void testHandlePackageUpdate_systemAppUpdate() {
+
+ // Package1 is a system app. Package 2 is not a system app, so it's not scanned
+ // in this test at all.
+ mSystemPackages.add(CALLING_PACKAGE_1);
+
+ // Initial state: no shortcuts.
+ mService.checkPackageChanges(USER_0);
+
+ assertEquals(mInjectedCurrentTimeMillis,
+ mService.getUserShortcutsLocked(USER_0).getLastAppScanTime());
+ assertEquals(mInjectedBuildFingerprint,
+ mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint());
+
+ // They have no shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ // Next.
+ // Update the packages -- now they have 1 manifest shortcut.
+ // But checkPackageChanges() don't notice it, since their version code / timestamp haven't
+ // changed.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ mInjectedCurrentTimeMillis += 1000;
+ mService.checkPackageChanges(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ // Next.
+ // Update the build finger print. All system apps will be scanned now.
+ mInjectedBuildFingerprint = "update1";
+ mInjectedCurrentTimeMillis += 1000;
+ mService.checkPackageChanges(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ // Next.
+ // Update manifest shortcuts.
+ mInjectedBuildFingerprint = "update2";
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ mInjectedCurrentTimeMillis += 1000;
+ mService.checkPackageChanges(USER_0);
+
+ // Fingerprint hasn't changed, so CALLING_PACKAGE_1 wasn't scanned.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ // Update the fingerprint, but CALLING_PACKAGE_1's version code hasn't changed, so
+ // still not scanned.
+ mInjectedBuildFingerprint = "update2";
+ mInjectedCurrentTimeMillis += 1000;
+ mService.checkPackageChanges(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ // Now update the version code, so CALLING_PACKAGE_1 is scanned again.
+ mInjectedBuildFingerprint = "update3";
+ mInjectedCurrentTimeMillis += 1000;
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.checkPackageChanges(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .isEmpty();
+ });
+
+ // Make sure getLastAppScanTime / getLastAppScanOsFingerprint are persisted.
+ initService();
+ assertEquals(mInjectedCurrentTimeMillis,
+ mService.getUserShortcutsLocked(USER_0).getLastAppScanTime());
+ assertEquals(mInjectedBuildFingerprint,
+ mService.getUserShortcutsLocked(USER_0).getLastAppScanOsFingerprint());
+ }
+
public void testHandlePackageChanged() {
final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1");
final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2");
@@ -5234,6 +5359,12 @@
/**
* It's the case with preintalled apps -- when applyRestore() is called, the system
* apps are already installed, so manifest shortcuts need to be re-published.
+ *
+ * Also, when a restore target app is already installed, and
+ * - if it has allowBackup=true, we'll restore normally, so all existing shortcuts will be
+ * replaced. (but manifest shortcuts will be re-published anyway.) We log a warning on
+ * logcat.
+ * - if it has allowBackup=false, we don't touch any of the existing shortcuts.
*/
public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
// Pre-backup. Same as testBackupAndRestore_manifestRePublished().
@@ -5265,6 +5396,19 @@
mService.mPackageMonitor.onReceive(mServiceContext,
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ // Set up shortcuts for package 3, which won't be backed up / restored.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_3, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_3, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertTrue(getManager().setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+
// Make sure the manifest shortcuts have been published.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertWith(getCallerShortcuts())
@@ -5290,6 +5434,11 @@
.areAllDisabled();
});
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "ms1");
+ });
+
// Backup and *without restarting the service, just call applyRestore()*.
{
int prevUid = mInjectedCallingUid;
@@ -5329,6 +5478,12 @@
.areAllNotDynamic()
;
});
+
+ // Package 3 still has the same shortcuts.
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("s1", "ms1");
+ });
}
public void testSaveAndLoad_crossProfile() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 43d2a1f..a04034e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -535,6 +535,18 @@
+ " user=" + userHandle);
}
+ ComponentName getCurAssistant(int userHandle) {
+ String curAssistant = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSISTANT, userHandle);
+ if (TextUtils.isEmpty(curAssistant)) {
+ return null;
+ }
+ if (DEBUG) Slog.d(TAG, "getCurAssistant curAssistant=" + curAssistant
+ + " user=" + userHandle);
+ return ComponentName.unflattenFromString(curAssistant);
+ }
+
void resetCurAssistant(int userHandle) {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ASSISTANT, null, userHandle);
@@ -1178,6 +1190,7 @@
synchronized (VoiceInteractionManagerServiceStub.this) {
ComponentName curInteractor = getCurInteractor(userHandle);
ComponentName curRecognizer = getCurRecognizer(userHandle);
+ ComponentName curAssistant = getCurAssistant(userHandle);
if (curRecognizer == null) {
// Could a new recognizer appear when we don't have one pre-installed?
if (anyPackagesAppearing()) {
@@ -1196,6 +1209,7 @@
// the default config.
setCurInteractor(null, userHandle);
setCurRecognizer(null, userHandle);
+ resetCurAssistant(userHandle);
initForUser(userHandle);
return;
}
@@ -1212,6 +1226,20 @@
return;
}
+ if (curAssistant != null) {
+ int change = isPackageDisappearing(curAssistant.getPackageName());
+ if (change == PACKAGE_PERMANENT_CHANGE) {
+ // If the currently set assistant is being removed, then we should
+ // reset back to the default state (which is probably that we prefer
+ // to have the default full voice interactor enabled).
+ setCurInteractor(null, userHandle);
+ setCurRecognizer(null, userHandle);
+ resetCurAssistant(userHandle);
+ initForUser(userHandle);
+ return;
+ }
+ }
+
// There is no interactor, so just deal with a simple recognizer.
int change = isPackageDisappearing(curRecognizer.getPackageName());
if (change == PACKAGE_PERMANENT_CHANGE
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index a093d54..2eb37df 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -327,7 +327,7 @@
*
* @hide
*/
- public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
+ public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1<<0;
/**
* Whether the call is a generic conference, where we do not know the precise state of
@@ -655,8 +655,8 @@
builder.append("Properties:");
}
- if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
- builder.append(isLong ? " PROPERTY_SHOW_CALLBACK_NUMBER" : " clbk");
+ if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
+ builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
}
if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index dc9767c..d6a2ee3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -870,6 +870,11 @@
/**
* Flag indicating whether the carrier supports the Hold command while in an IMS call.
+ * <p>
+ * The device configuration value {@code config_device_respects_hold_carrier_config} ultimately
+ * controls whether this carrier configuration option is used. Where
+ * {@code config_device_respects_hold_carrier_config} is false, the value of the
+ * {@link #KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} carrier configuration option is ignored.
* @hide
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
diff --git a/telephony/java/android/telephony/Rlog.java b/telephony/java/android/telephony/Rlog.java
index b4f400f..cd0a012 100644
--- a/telephony/java/android/telephony/Rlog.java
+++ b/telephony/java/android/telephony/Rlog.java
@@ -16,8 +16,15 @@
package android.telephony;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.Base64;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
/**
* A class to log strings to the RADIO LOG.
*
@@ -87,11 +94,52 @@
/**
* Redact personally identifiable information for production users.
- * If log tag is loggable in verbose mode, return the original string, otherwise return XXX.
+ * @param tag used to identify the source of a log message
+ * @param pii the personally identifiable information we want to apply secure hash on.
+ * @return If tag is loggable in verbose mode or pii is null, return the original input.
+ * otherwise return a secure Hash of input pii
*/
public static String pii(String tag, Object pii) {
- return (isLoggable(tag, Log.VERBOSE) ? String.valueOf(pii) : "XXX");
+ String val = String.valueOf(pii);
+ if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) {
+ return val;
+ }
+ return "[" + secureHash(val.getBytes()) + "]";
}
+ /**
+ * Redact personally identifiable information for production users.
+ * @param enablePiiLogging set when caller explicitly want to enable sensitive logging.
+ * @param pii the personally identifiable information we want to apply secure hash on.
+ * @return If enablePiiLogging is set to true or pii is null, return the original input.
+ * otherwise return a secure Hash of input pii
+ */
+ public static String pii(boolean enablePiiLogging, Object pii) {
+ String val = String.valueOf(pii);
+ if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) {
+ return val;
+ }
+ return "[" + secureHash(val.getBytes()) + "]";
+ }
+
+ /**
+ * Returns a secure hash (using the SHA1 algorithm) of the provided input.
+ *
+ * @return the hash
+ * @param input the bytes for which the secure hash should be computed.
+ */
+ private static String secureHash(byte[] input) {
+ MessageDigest messageDigest;
+
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return "####";
+ }
+
+ byte[] result = messageDigest.digest(input);
+ return Base64.encodeToString(
+ result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 6229ed9..cf2d27e 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -340,7 +340,7 @@
String iccIdToPrint = null;
if (iccId != null) {
if (iccId.length() > 9 && !Build.IS_DEBUGGABLE) {
- iccIdToPrint = iccId.substring(0, 9) + "XXXXXXXXXXX";
+ iccIdToPrint = iccId.substring(0, 9) + Rlog.pii(false, iccId.substring(9));
} else {
iccIdToPrint = iccId;
}
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index 2956d87..7ec46a3e 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -256,8 +256,8 @@
def check_emoji_coverage(all_emoji, equivalent_emoji):
- emoji_font = get_emoji_font()
- check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji)
+ emoji_font = get_emoji_font()
+ check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji)
def get_emoji_font():
@@ -274,15 +274,12 @@
assert sequence in coverage, (
'%s is not supported in the emoji font.' % printable(sequence))
- # disable temporarily - we cover more than this
- """
for sequence in coverage:
if sequence in {0x0000, 0x000D, 0x0020}:
# The font needs to support a few extra characters, which is OK
continue
assert sequence in all_emoji, (
'Emoji font should not support %s.' % printable(sequence))
- """
for first, second in sorted(equivalent_emoji.items()):
assert coverage[first] == coverage[second], (
@@ -290,8 +287,6 @@
printable(first),
printable(second)))
- # disable temporarily - some equivalent sequences we don't even know about
- """
for glyph in set(coverage.values()):
maps_to_glyph = [seq for seq in coverage if coverage[seq] == glyph]
if len(maps_to_glyph) > 1:
@@ -307,7 +302,7 @@
'The sequences %s should not result in the same glyph %s' % (
printable(equivalent_seqs),
glyph))
- """
+
def check_emoji_defaults(default_emoji):
missing_text_chars = _emoji_properties['Emoji'] - default_emoji
@@ -334,15 +329,9 @@
# Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
# webdings yet.
missing_text_chars -= _chars_by_age['7.0']
- # TODO: Remove these after b/26113320 is fixed
- missing_text_chars -= {
- 0x263A, # WHITE SMILING FACE
- 0x270C, # VICTORY HAND
- 0x2744, # SNOWFLAKE
- 0x2764, # HEAVY BLACK HEART
- }
assert missing_text_chars == set(), (
- 'Text style version of some emoji characters are missing: ' + repr(missing_text_chars))
+ 'Text style version of some emoji characters are missing: ' +
+ repr(missing_text_chars))
# Setting reverse to true returns a dictionary that maps the values to sets of
@@ -362,7 +351,7 @@
if not line:
continue
- chars, prop = line.split(';')
+ chars, prop = line.split(';')[:2]
chars = chars.strip()
prop = prop.strip()
@@ -423,26 +412,6 @@
_emoji_zwj_sequences = parse_unicode_datafile(
path.join(ucd_path, 'emoji-zwj-sequences.txt'))
- # filter modern pentathlon, as it seems likely to be removed from final spec
- # also filter rifle
- def is_excluded(n):
- return n in [0x1f93b, 0x1f946]
-
- def contains_excluded(t):
- if type(t) == int:
- return is_excluded(t)
- return any(is_excluded(cp) for cp in t)
-
- # filter modern pentathlon, as it seems likely to be removed from final spec
- _emoji_properties['Emoji'] = set(
- t for t in _emoji_properties['Emoji'] if not contains_excluded(t))
- _emoji_sequences = dict(
- (t, v) for (t, v) in _emoji_sequences.items() if not contains_excluded(t))
-
- # add in UN flag
- UN_seq = flag_sequence('UN')
- _emoji_sequences[UN_seq] = 'Emoji_Flag_Sequence'
-
def flag_sequence(territory_code):
return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
@@ -454,7 +423,8 @@
flag_sequence('GF'), flag_sequence('GP'), flag_sequence('GS'),
flag_sequence('MF'), flag_sequence('MQ'), flag_sequence('NC'),
flag_sequence('PM'), flag_sequence('RE'), flag_sequence('TF'),
- flag_sequence('WF'), flag_sequence('XK'), flag_sequence('YT'),
+ flag_sequence('UN'), flag_sequence('WF'), flag_sequence('XK'),
+ flag_sequence('YT'),
})
EQUIVALENT_FLAGS = {
@@ -467,6 +437,22 @@
COMBINING_KEYCAP = 0x20E3
+# Characters that Android defaults to emoji style, different from the recommendations in UTR #51
+ANDROID_DEFAULT_EMOJI = frozenset({
+ 0x2600, # BLACK SUN WITH RAYS
+ 0x2601, # CLOUD
+ 0x260E, # BLACK TELEPHONE
+ 0x261D, # WHITE UP POINTING INDEX
+ 0x263A, # WHITE SMILING FACE
+ 0x2660, # BLACK SPADE SUIT
+ 0x2663, # BLACK CLUB SUIT
+ 0x2665, # BLACK HEART SUIT
+ 0x2666, # BLACK DIAMOND SUIT
+ 0x270C, # VICTORY HAND
+ 0x2744, # SNOWFLAKE
+ 0x2764, # HEAVY BLACK HEART
+})
+
LEGACY_ANDROID_EMOJI = {
0xFE4E5: flag_sequence('JP'),
0xFE4E6: flag_sequence('US'),
@@ -502,7 +488,17 @@
def is_fitzpatrick_modifier(cp):
- return 0x1f3fb <= cp <= 0x1f3ff
+ return 0x1F3FB <= cp <= 0x1F3FF
+
+
+def reverse_emoji(seq):
+ rev = list(reversed(seq))
+ # if there are fitzpatrick modifiers in the sequence, keep them after
+ # the emoji they modify
+ for i in xrange(1, len(rev)):
+ if is_fitzpatrick_modifier(rev[i-1]):
+ rev[i], rev[i-1] = rev[i-1], rev[i]
+ return tuple(rev)
def compute_expected_emoji():
@@ -511,26 +507,52 @@
all_sequences = set()
all_sequences.update(_emoji_variation_sequences)
+ # add zwj sequences not in the current emoji-zwj-sequences.txt
+ adjusted_emoji_zwj_sequences = dict(_emoji_zwj_sequences)
+ adjusted_emoji_zwj_sequences.update(_emoji_zwj_sequences)
+ # single parent families
+ additional_emoji_zwj = (
+ (0x1F468, 0x200D, 0x1F466),
+ (0x1F468, 0x200D, 0x1F467),
+ (0x1F468, 0x200D, 0x1F466, 0x200D, 0x1F466),
+ (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F466),
+ (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F467),
+ (0x1F469, 0x200D, 0x1F466),
+ (0x1F469, 0x200D, 0x1F467),
+ (0x1F469, 0x200D, 0x1F466, 0x200D, 0x1F466),
+ (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F466),
+ (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F467),
+ )
+ # sequences formed from man and woman and optional fitzpatrick modifier
+ modified_extensions = (
+ 0x2696,
+ 0x2708,
+ 0x1F3A8,
+ 0x1F680,
+ 0x1F692,
+ )
+ for seq in additional_emoji_zwj:
+ adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
+ for ext in modified_extensions:
+ for base in (0x1F468, 0x1F469):
+ seq = (base, 0x200D, ext)
+ adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
+ for modifier in range(0x1F3FB, 0x1F400):
+ seq = (base, modifier, 0x200D, ext)
+ adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
+
for sequence in _emoji_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
- for sequence in _emoji_zwj_sequences.keys():
+ for sequence in adjusted_emoji_zwj_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
# Add reverse of all emoji ZWJ sequences, which are added to the fonts
# as a workaround to get the sequences work in RTL text.
- reversed_seq = list(reversed(sequence))
- # if there are fitzpatrick modifiers in the sequence, keep them after
- # the emoji they modify
- for i in xrange(1, len(reversed_seq)):
- if is_fitzpatrick_modifier(reversed_seq[i - 1]):
- tmp = reversed_seq[i]
- reversed_seq[i] = reversed_seq[i-1]
- reversed_seq[i-1] = tmp
- reversed_seq = tuple(reversed_seq)
+ reversed_seq = reverse_emoji(sequence)
all_sequences.add(reversed_seq)
equivalent_emoji[reversed_seq] = sequence
@@ -549,6 +571,7 @@
set(LEGACY_ANDROID_EMOJI.keys()))
default_emoji = (
_emoji_properties['Emoji_Presentation'] |
+ ANDROID_DEFAULT_EMOJI |
all_sequences |
set(LEGACY_ANDROID_EMOJI.keys()))
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 9d0c20c..d3d5ea0 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -817,6 +817,7 @@
*/
public static final int NETWORK_SELECTION_ENABLE = 0;
/**
+ * @deprecated it is not used any more.
* This network is disabled because higher layer (>2) network is bad
*/
public static final int DISABLED_BAD_LINK = 1;
@@ -862,7 +863,7 @@
*/
private static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
"NETWORK_SELECTION_ENABLE",
- "NETWORK_SELECTION_DISABLED_BAD_LINK",
+ "NETWORK_SELECTION_DISABLED_BAD_LINK", // deprecated
"NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ",
"NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE",
"NETWORK_SELECTION_DISABLED_DHCP_FAILURE",
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index bbc3d2f..1633bd9c9 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -49,6 +49,8 @@
* This class provides the primary API for managing all aspects of Wi-Fi
* connectivity. Get an instance of this class by calling
* {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
+ * On releases before NYC, it should only be obtained from an application context, and not from
+ * any other derived context to avoid memory leaks within the calling process.
* It deals with several categories of items:
* <ul>