summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/accounts/AccountManager.java38
-rw-r--r--core/java/android/app/Activity.java7
-rw-r--r--core/java/android/app/ActivityManager.java22
-rw-r--r--core/java/android/app/ActivityThread.java6
-rw-r--r--core/java/android/app/AppOpsManager.java134
-rw-r--r--core/java/android/app/ExitTransitionCoordinator.java2
-rw-r--r--core/java/android/app/Fragment.java6
-rw-r--r--core/java/android/app/FragmentHostCallback.java17
-rw-r--r--core/java/android/app/FragmentManager.java8
-rw-r--r--core/java/android/app/IntentService.java2
-rw-r--r--core/java/android/app/Notification.java472
-rw-r--r--core/java/android/app/backup/BackupAgent.java276
-rw-r--r--core/java/android/app/backup/FullBackup.java401
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java3
-rw-r--r--core/java/android/bluetooth/BluetoothHealthCallback.java3
-rw-r--r--core/java/android/bluetooth/BluetoothSap.java59
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java10
-rw-r--r--core/java/android/bluetooth/le/ScanCallback.java6
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java11
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java23
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl4
-rw-r--r--core/java/android/content/pm/PackageParser.java18
-rw-r--r--core/java/android/hardware/Camera.java5
-rw-r--r--core/java/android/hardware/ICameraService.aidl6
-rw-r--r--core/java/android/hardware/SystemSensorManager.java80
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java2
-rw-r--r--core/java/android/net/ConnectivityManager.java4
-rw-r--r--core/java/android/os/AsyncTask.java13
-rw-r--r--core/java/android/os/Bundle.java8
-rw-r--r--core/java/android/os/IPermissionController.aidl1
-rw-r--r--core/java/android/os/storage/StorageManager.java2
-rw-r--r--core/java/android/preference/GenericInflater.java1
-rw-r--r--core/java/android/provider/ContactsContract.java10
-rw-r--r--core/java/android/service/carrier/CarrierMessagingService.java77
-rw-r--r--core/java/android/service/carrier/ICarrierMessagingService.aidl9
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java13
-rw-r--r--core/java/android/service/voice/VoiceInteractionServiceInfo.java10
-rw-r--r--core/java/android/text/DynamicLayout.java5
-rw-r--r--core/java/android/text/StaticLayout.java161
-rw-r--r--core/java/android/transition/Transition.java3
-rw-r--r--core/java/android/transition/TransitionInflater.java2
-rw-r--r--core/java/android/transition/Visibility.java117
-rw-r--r--core/java/android/view/DisplayListCanvas.java6
-rw-r--r--core/java/android/view/GhostView.java6
-rw-r--r--core/java/android/view/LayoutInflater.java2
-rw-r--r--core/java/android/view/MenuInflater.java1
-rw-r--r--core/java/android/view/ThreadedRenderer.java32
-rw-r--r--core/java/android/view/View.java66
-rw-r--r--core/java/android/view/ViewGroup.java7
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java28
-rw-r--r--core/java/android/widget/AbsListView.java8
-rw-r--r--core/java/android/widget/CursorAdapter.java2
-rw-r--r--core/java/android/widget/Editor.java14
-rw-r--r--core/java/android/widget/HorizontalScrollView.java14
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java10
-rw-r--r--core/java/android/widget/ScrollView.java13
-rw-r--r--core/java/android/widget/TextView.java12
58 files changed, 1415 insertions, 869 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index ffa36d6..fd40d99 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,6 +16,8 @@
package android.accounts;
+import android.annotation.RequiresPermission;
+import android.annotation.Size;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -49,6 +51,11 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import static android.Manifest.permission.AUTHENTICATE_ACCOUNTS;
+import static android.Manifest.permission.GET_ACCOUNTS;
+import static android.Manifest.permission.MANAGE_ACCOUNTS;
+import static android.Manifest.permission.USE_CREDENTIALS;
+
/**
* This class provides access to a centralized registry of the user's
* online accounts. The user enters credentials (username and password) once
@@ -319,6 +326,7 @@ public class AccountManager {
* @param account The account to query for a password
* @return The account's password, null if none or if the account doesn't exist
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String getPassword(final Account account) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -344,6 +352,7 @@ public class AccountManager {
* @param account The account to query for user data
* @return The user data, null if the account or key doesn't exist
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String getUserData(final Account account, final String key) {
if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null");
@@ -409,6 +418,7 @@ public class AccountManager {
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccounts() {
try {
return mService.getAccounts(null);
@@ -431,6 +441,7 @@ public class AccountManager {
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsAsUser(int userId) {
try {
return mService.getAccountsAsUser(null, userId);
@@ -490,6 +501,7 @@ public class AccountManager {
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, Process.myUserHandle());
}
@@ -576,6 +588,7 @@ public class AccountManager {
* @return An {@link AccountManagerFuture} which resolves to a Boolean,
* true if the account exists and has all of the specified features.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public AccountManagerFuture<Boolean> hasFeatures(final Account account,
final String[] features,
AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -621,6 +634,7 @@ public class AccountManager {
* {@link Account}, one per account of the specified type which
* matches the requested features.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
final String type, final String[] features,
AccountManagerCallback<Account[]> callback, Handler handler) {
@@ -659,6 +673,7 @@ public class AccountManager {
* @return True if the account was successfully added, false if the account
* already exists, the account is null, or another error occurs.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -684,6 +699,7 @@ public class AccountManager {
*
* @param account The {@link Account} to be updated.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean notifyAccountAuthenticated(Account account) {
if (account == null)
throw new IllegalArgumentException("account is null");
@@ -715,9 +731,10 @@ public class AccountManager {
* after the name change. If successful the account's name will be the
* specified new name.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public AccountManagerFuture<Account> renameAccount(
final Account account,
- final String newName,
+ @Size(min = 1) final String newName,
AccountManagerCallback<Account> callback,
Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null.");
@@ -783,6 +800,7 @@ public class AccountManager {
* {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)}
* instead
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
@Deprecated
public AccountManagerFuture<Boolean> removeAccount(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -837,6 +855,7 @@ public class AccountManager {
* adding accounts (of this type) has been disabled by policy
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> removeAccount(final Account account,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
@@ -909,6 +928,7 @@ public class AccountManager {
* account did not exist, the account is null, or another error
* occurs.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean removeAccountExplicitly(Account account) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -935,6 +955,7 @@ public class AccountManager {
* @param accountType The account type of the auth token to invalidate, must not be null
* @param authToken The auth token to invalidate, may be null
*/
+ @RequiresPermission(anyOf = {MANAGE_ACCOUNTS, USE_CREDENTIALS})
public void invalidateAuthToken(final String accountType, final String authToken) {
if (accountType == null) throw new IllegalArgumentException("accountType is null");
try {
@@ -964,6 +985,7 @@ public class AccountManager {
* @return The cached auth token for this account and type, or null if
* no auth token is cached or the account does not exist.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String peekAuthToken(final Account account, final String authTokenType) {
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -990,6 +1012,7 @@ public class AccountManager {
* @param account The account to set a password for
* @param password The password to set, null to clear the password
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setPassword(final Account account, final String password) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -1014,6 +1037,7 @@ public class AccountManager {
*
* @param account The account whose password to clear
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public void clearPassword(final Account account) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -1039,6 +1063,7 @@ public class AccountManager {
* @param key The userdata key to set. Must not be null
* @param value The value to set, null to clear this userdata key
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setUserData(final Account account, final String key, final String value) {
if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null");
@@ -1066,6 +1091,7 @@ public class AccountManager {
* @param authTokenType The type of the auth token, see {#getAuthToken}
* @param authToken The auth token to add to the cache
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setAuthToken(Account account, final String authTokenType, final String authToken) {
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1100,6 +1126,7 @@ public class AccountManager {
* @throws java.io.IOException if the authenticator experienced an I/O problem
* creating a new auth token, usually because of network trouble
*/
+ @RequiresPermission(USE_CREDENTIALS)
public String blockingGetAuthToken(Account account, String authTokenType,
boolean notifyAuthFailure)
throws OperationCanceledException, IOException, AuthenticatorException {
@@ -1174,6 +1201,7 @@ public class AccountManager {
* authenticator-dependent. The caller should verify the validity of the
* account before requesting an auth token.
*/
+ @RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
@@ -1264,6 +1292,7 @@ public class AccountManager {
* boolean, AccountManagerCallback, android.os.Handler)} instead
*/
@Deprecated
+ @RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType,
final boolean notifyAuthFailure,
@@ -1342,6 +1371,7 @@ public class AccountManager {
* authenticator-dependent. The caller should verify the validity of the
* account before requesting an auth token.
*/
+ @RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
final boolean notifyAuthFailure,
@@ -1411,6 +1441,7 @@ public class AccountManager {
* creating a new account, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> addAccount(final String accountType,
final String authTokenType, final String[] requiredFeatures,
final Bundle addAccountOptions,
@@ -1598,6 +1629,7 @@ public class AccountManager {
* verifying the password, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
final Bundle options,
final Activity activity,
@@ -1674,6 +1706,7 @@ public class AccountManager {
* verifying the password, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> updateCredentials(final Account account,
final String authTokenType,
final Bundle options, final Activity activity,
@@ -1725,6 +1758,7 @@ public class AccountManager {
* updating settings, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> editProperties(final String accountType,
final Activity activity, final AccountManagerCallback<Bundle> callback,
final Handler handler) {
@@ -2258,6 +2292,7 @@ public class AccountManager {
* updating settings, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
final String accountType, final String authTokenType, final String[] features,
final Activity activity, final Bundle addAccountOptions,
@@ -2382,6 +2417,7 @@ public class AccountManager {
* @throws IllegalArgumentException if listener is null
* @throws IllegalStateException if listener was already added
*/
+ @RequiresPermission(GET_ACCOUNTS)
public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
Handler handler, boolean updateImmediately) {
if (listener == null) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e79e20c..7260d10 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -21,6 +21,7 @@ import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.LayoutRes;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
@@ -891,6 +892,7 @@ public class Activity extends ContextThemeWrapper
* @see #onRestoreInstanceState
* @see #onPostCreate
*/
+ @MainThread
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
@@ -6496,6 +6498,11 @@ public class Activity extends ContextThemeWrapper
return (w == null) ? 0 : w.getAttributes().windowAnimations;
}
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ Activity.this.onAttachFragment(fragment);
+ }
+
@Nullable
@Override
public View onFindViewById(int id) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 576a046..9bbb4be 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,8 +16,10 @@
package android.app;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -26,6 +28,7 @@ import android.os.BatteryStats;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
@@ -2396,7 +2399,24 @@ public class ActivityManager {
} catch (RemoteException e) {
}
}
-
+
+ /**
+ * Kills the specified UID.
+ * @param uid The UID to kill.
+ * @param reason The reason for the kill.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.KILL_UID)
+ public void killUid(int uid, String reason) {
+ try {
+ ActivityManagerNative.getDefault().killUid(uid, reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't kill uid:" + uid, e);
+ }
+ }
+
/**
* Have the system perform a force stop of everything associated with
* the given application package. All processes that share its uid
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2e45b79..da6d8c5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1638,6 +1638,12 @@ public final class ActivityThread {
return sCurrentActivityThread;
}
+ public static String currentOpPackageName() {
+ ActivityThread am = currentActivityThread();
+ return (am != null && am.getApplication() != null)
+ ? am.getApplication().getOpPackageName() : null;
+ }
+
public static String currentPackageName() {
ActivityThread am = currentActivityThread();
return (am != null && am.mBoundApplication != null)
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8a3c9c8..5aa399b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -223,8 +223,12 @@ public class AppOpsManager {
public static final int OP_PROCESS_OUTGOING_CALLS = 54;
/** @hide User the fingerprint API. */
public static final int OP_USE_FINGERPRINT = 55;
+ /** @hide Access to body sensors such as heart rate, etc. */
+ public static final int OP_BODY_SENSORS = 56;
+ /** @hide Read previously received cell broadcast messages. */
+ public static final int OP_READ_CELL_BROADCASTS = 57;
/** @hide */
- public static final int _NUM_OP = 56;
+ public static final int _NUM_OP = 58;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -280,9 +284,6 @@ public class AppOpsManager {
/** @hide Allows an application to send SMS messages. */
public static final String OPSTR_SEND_SMS
= "android:send_sms";
- /** @hide Allows an application to add system alert windows. */
- public static final String OPSTR_SYSTEM_ALERT_WINDOW
- = "android:system_alert_window";
/** @hide Required to be able to access the camera device. */
public static final String OPSTR_CAMERA
= "android:camera";
@@ -295,6 +296,18 @@ public class AppOpsManager {
/** @hide Required to access phone state related information. */
public static final String OPSTR_ADD_VOICEMAIL
= "android:add_voicemail";
+ /** @hide Access APIs for SIP calling over VOIP or WiFi */
+ public static final String OPSTR_USE_SIP
+ = "android:use_sip";
+ /** @hide Use the fingerprint API. */
+ public static final String OPSTR_USE_FINGERPRINT
+ = "android:use_fingerprint";
+ /** @hide Access to body sensors such as heart rate, etc. */
+ public static final String OPSTR_BODY_SENSORS
+ = "android:body_sensors";
+ /** @hide Read previously received cell broadcast messages. */
+ public static final String OPSTR_READ_CELL_BROADCASTS
+ = "android:read_cell_broadcasts";
/**
* This maps each operation to the operation that serves as the
@@ -360,7 +373,9 @@ public class AppOpsManager {
OP_ADD_VOICEMAIL,
OP_USE_SIP,
OP_PROCESS_OUTGOING_CALLS,
- OP_USE_FINGERPRINT
+ OP_USE_FINGERPRINT,
+ OP_BODY_SENSORS,
+ OP_READ_CELL_BROADCASTS
};
/**
@@ -372,30 +387,30 @@ public class AppOpsManager {
OPSTR_FINE_LOCATION,
null,
null,
+ OPSTR_READ_CONTACTS,
+ OPSTR_WRITE_CONTACTS,
+ OPSTR_READ_CALL_LOG,
+ OPSTR_WRITE_CALL_LOG,
+ OPSTR_READ_CALENDAR,
+ OPSTR_WRITE_CALENDAR,
null,
null,
null,
+ OPSTR_CALL_PHONE,
+ OPSTR_READ_SMS,
null,
+ OPSTR_RECEIVE_SMS,
null,
+ OPSTR_RECEIVE_MMS,
+ OPSTR_RECEIVE_WAP_PUSH,
+ OPSTR_SEND_SMS,
null,
null,
null,
null,
null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
+ OPSTR_CAMERA,
+ OPSTR_RECORD_AUDIO,
null,
null,
null,
@@ -419,11 +434,13 @@ public class AppOpsManager {
null,
null,
null,
+ OPSTR_READ_PHONE_STATE,
+ OPSTR_ADD_VOICEMAIL,
+ OPSTR_USE_SIP,
null,
- null,
- null,
- null,
- null
+ OPSTR_USE_FINGERPRINT,
+ OPSTR_BODY_SENSORS,
+ OPSTR_READ_CELL_BROADCASTS
};
/**
@@ -486,7 +503,9 @@ public class AppOpsManager {
"ADD_VOICEMAIL",
"USE_SIP",
"PROCESS_OUTGOING_CALLS",
- "USE_FINGERPRINT"
+ "USE_FINGERPRINT",
+ "BODY_SENSORS",
+ "READ_CELL_BROADCASTS"
};
/**
@@ -549,7 +568,9 @@ public class AppOpsManager {
Manifest.permission.ADD_VOICEMAIL,
Manifest.permission.USE_SIP,
Manifest.permission.PROCESS_OUTGOING_CALLS,
- Manifest.permission.USE_FINGERPRINT
+ Manifest.permission.USE_FINGERPRINT,
+ Manifest.permission.BODY_SENSORS,
+ Manifest.permission.READ_CELL_BROADCASTS
};
/**
@@ -613,7 +634,9 @@ public class AppOpsManager {
null, // ADD_VOICEMAIL
null, // USE_SIP
null, // PROCESS_OUTGOING_CALLS
- null // USE_FINGERPRINT
+ null, // USE_FINGERPRINT
+ null, // BODY_SENSORS
+ null // READ_CELL_BROADCASTS
};
/**
@@ -676,7 +699,9 @@ public class AppOpsManager {
false, //ADD_VOICEMAIL
false, // USE_SIP
false, // PROCESS_OUTGOING_CALLS
- false // USE_FINGERPRINT
+ false, // USE_FINGERPRINT
+ false, // BODY_SENSORS
+ false // READ_CELL_BROADCASTS
};
/**
@@ -738,6 +763,8 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED
};
@@ -804,37 +831,20 @@ public class AppOpsManager {
false,
false,
false,
+ false,
+ false,
false
};
/**
- * This is a mapping from a permission name to public app op name.
+ * Mapping from an app op name to the app op code.
*/
- private static final ArrayMap<String, String> sPermToOp = new ArrayMap<>();
- static {
- sPermToOp.put(Manifest.permission.ACCESS_COARSE_LOCATION, OPSTR_COARSE_LOCATION);
- sPermToOp.put(Manifest.permission.ACCESS_FINE_LOCATION, OPSTR_FINE_LOCATION);
- sPermToOp.put(Manifest.permission.PACKAGE_USAGE_STATS, OPSTR_GET_USAGE_STATS);
- sPermToOp.put(Manifest.permission.READ_CONTACTS, OPSTR_READ_CONTACTS);
- sPermToOp.put(Manifest.permission.WRITE_CONTACTS, OPSTR_WRITE_CONTACTS);
- sPermToOp.put(Manifest.permission.READ_CALL_LOG, OPSTR_READ_CALL_LOG);
- sPermToOp.put(Manifest.permission.WRITE_CALL_LOG, OPSTR_WRITE_CALL_LOG);
- sPermToOp.put(Manifest.permission.READ_CALENDAR, OPSTR_READ_CALENDAR);
- sPermToOp.put(Manifest.permission.WRITE_CALENDAR, OPSTR_WRITE_CALENDAR);
- sPermToOp.put(Manifest.permission.CALL_PHONE, OPSTR_CALL_PHONE);
- sPermToOp.put(Manifest.permission.READ_SMS, OPSTR_READ_SMS);
- sPermToOp.put(Manifest.permission.RECEIVE_SMS, OPSTR_RECEIVE_SMS);
- sPermToOp.put(Manifest.permission.RECEIVE_MMS, OPSTR_RECEIVE_MMS);
- sPermToOp.put(Manifest.permission.RECEIVE_WAP_PUSH, OPSTR_RECEIVE_WAP_PUSH);
- sPermToOp.put(Manifest.permission.SEND_SMS, OPSTR_SEND_SMS);
- sPermToOp.put(Manifest.permission.SYSTEM_ALERT_WINDOW, OPSTR_SYSTEM_ALERT_WINDOW);
- sPermToOp.put(Manifest.permission.CAMERA, OPSTR_CAMERA);
- sPermToOp.put(Manifest.permission.RECORD_AUDIO, OPSTR_RECORD_AUDIO);
- sPermToOp.put(Manifest.permission.READ_PHONE_STATE, OPSTR_READ_PHONE_STATE);
- sPermToOp.put(Manifest.permission.ADD_VOICEMAIL, OPSTR_ADD_VOICEMAIL);
- }
+ private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
- private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
+ /**
+ * Mapping from a permission to the corresponding app op.
+ */
+ private static HashMap<String, Integer> sPermToOp = new HashMap<>();
static {
if (sOpToSwitch.length != _NUM_OP) {
@@ -874,6 +884,11 @@ public class AppOpsManager {
sOpStrToOp.put(sOpToString[i], i);
}
}
+ for (int i=0; i<_NUM_OP; i++) {
+ if (sOpPerms[i] != null) {
+ sPermToOp.put(sOpPerms[i], i);
+ }
+ }
}
/**
@@ -922,6 +937,15 @@ public class AppOpsManager {
}
/**
+ * Retrieve the app op code for a permission, or null if there is not one.
+ * @hide
+ */
+ public static int permissionToOpCode(String permission) {
+ Integer boxedOpCode = sPermToOp.get(permission);
+ return boxedOpCode != null ? boxedOpCode : OP_NONE;
+ }
+
+ /**
* Retrieve whether the op allows the system (and system ui) to
* bypass the user restriction.
* @hide
@@ -1185,7 +1209,11 @@ public class AppOpsManager {
*/
@SystemApi
public static String permissionToOp(String permission) {
- return sPermToOp.get(permission);
+ final Integer opCode = sPermToOp.get(permission);
+ if (opCode == null) {
+ return null;
+ }
+ return sOpToString[opCode];
}
/**
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 169952a..0f286fb 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -239,7 +239,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
}
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
- mAllSharedElementNames, resultCode, data);
+ mSharedElementNames, resultCode, data);
mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
@Override
public void onTranslucentConversionComplete(boolean drawComplete) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 91d810e..40c5c64 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1314,6 +1314,12 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap, true);
}
a.recycle();
+
+ final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+ if (hostActivity != null) {
+ mCalled = false;
+ onInflate(hostActivity, attrs, savedInstanceState);
+ }
}
/**
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index dad2c79..3e753f0 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -23,7 +23,6 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.ArrayMap;
-import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -140,6 +139,14 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer {
return mWindowAnimations;
}
+ /**
+ * Called when a {@link Fragment} is being attached to this host, immediately
+ * after the call to its {@link Fragment#onAttach(Context)} method and before
+ * {@link Fragment#onCreate(Bundle)}.
+ */
+ public void onAttachFragment(Fragment fragment) {
+ }
+
@Nullable
@Override
public View onFindViewById(int id) {
@@ -187,14 +194,6 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer {
}
}
- void onFragmentInflate(Fragment fragment, AttributeSet attrs, Bundle savedInstanceState) {
- fragment.onInflate(mContext, attrs, savedInstanceState);
- }
-
- void onFragmentAttach(Fragment fragment) {
- fragment.onAttach(mContext);
- }
-
void doLoaderStart() {
if (mLoadersStarted) {
return;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 62436e9..6b5239d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -844,13 +844,13 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
f.mFragmentManager = mParent != null
? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
f.mCalled = false;
- mHost.onFragmentAttach(f);
+ f.onAttach(mHost.getContext());
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
- mHost.onFragmentAttach(f);
+ mHost.onAttachFragment(f);
}
if (!f.mRetaining) {
@@ -2107,7 +2107,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
- mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
addFragment(fragment, true);
} else if (fragment.mInLayout) {
// A fragment already exists and it is not one we restored from
@@ -2124,7 +2124,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
// from last saved state), then give it the attributes to
// initialize itself.
if (!fragment.mRetaining) {
- mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
}
}
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 96767ae..3cda973 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.WorkerThread;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
@@ -158,5 +159,6 @@ public abstract class IntentService extends Service {
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
*/
+ @WorkerThread
protected abstract void onHandleIntent(Intent intent);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2cf23af..49b2549 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2095,6 +2095,7 @@ public class Notification implements Parcelable
try {
Constructor<? extends Style> constructor = styleClass.getConstructor();
+ constructor.setAccessible(true);
style = constructor.newInstance();
style.restoreFromExtras(extras);
} catch (Throwable t) {
@@ -5493,477 +5494,6 @@ public class Notification implements Parcelable
}
/**
- * <p>
- * Helper class to add content info extensions to notifications. To create a notification with
- * content info extensions:
- * <ol>
- * <li>Create an {@link Notification.Builder}, setting any desired properties.
- * <li>Create a {@link ContentInfoExtender}.
- * <li>Set content info specific properties using the {@code add} and {@code set} methods of
- * {@link ContentInfoExtender}.
- * <li>Call {@link Notification.Builder#extend(Notification.Extender)} to apply the extensions
- * to a notification.
- * </ol>
- *
- * <pre class="prettyprint">Notification notification = new Notification.Builder(context) * ... * .extend(new ContentInfoExtender() * .set*(...)) * .build(); * </pre>
- * <p>
- * Content info extensions can be accessed on an existing notification by using the
- * {@code ContentInfoExtender(Notification)} constructor, and then using the {@code get} methods
- * to access values.
- */
- public static final class ContentInfoExtender implements Extender {
- private static final String TAG = "ContentInfoExtender";
-
- // Key for the Content info extensions bundle in the main Notification extras bundle
- private static final String EXTRA_CONTENT_INFO_EXTENDER = "android.CONTENT_INFO_EXTENSIONS";
-
- // Keys within EXTRA_CONTENT_INFO_EXTENDER for individual content info options.
-
- private static final String KEY_CONTENT_TYPE = "android.contentType";
-
- private static final String KEY_CONTENT_GENRES = "android.contentGenre";
-
- private static final String KEY_CONTENT_PRICING_TYPE = "android.contentPricing.type";
-
- private static final String KEY_CONTENT_PRICING_VALUE = "android.contentPricing.value";
-
- private static final String KEY_CONTENT_STATUS = "android.contentStatus";
-
- private static final String KEY_CONTENT_MATURITY_RATING = "android.contentMaturity";
-
- private static final String KEY_CONTENT_RUN_LENGTH = "android.contentLength";
-
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a video clip.
- */
- public static final String CONTENT_TYPE_VIDEO = "android.contentType.video";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a movie.
- */
- public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a trailer.
- */
- public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is serial. It can refer to an entire show, a single season or
- * series, or a single episode.
- */
- public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a song or album.
- */
- public static final String CONTENT_TYPE_MUSIC = "android.contentType.music";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a radio station.
- */
- public static final String CONTENT_TYPE_RADIO = "android.contentType.radio";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a podcast.
- */
- public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a news item.
- */
- public static final String CONTENT_TYPE_NEWS = "android.contentType.news";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is sports.
- */
- public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is an application.
- */
- public static final String CONTENT_TYPE_APP = "android.contentType.app";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a game.
- */
- public static final String CONTENT_TYPE_GAME = "android.contentType.game";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a book.
- */
- public static final String CONTENT_TYPE_BOOK = "android.contentType.book";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a comic book.
- */
- public static final String CONTENT_TYPE_COMIC = "android.contentType.comic";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a magazine.
- */
- public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a website.
- */
- public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website";
-
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is free to consume.
- */
- public static final String CONTENT_PRICING_FREE = "android.contentPrice.free";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available as a rental, and the price value provided
- * is the rental price for the item.
- */
- public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available for purchase, and the price value provided
- * is the purchase price for the item.
- */
- public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available currently as a pre-order, and the price
- * value provided is the purchase price for the item.
- */
- public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available as part of a subscription based service,
- * and the price value provided is the subscription price for the service.
- */
- public static final String CONTENT_PRICING_SUBSCRIPTION =
- "android.contentPrice.subscription";
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is available and ready to be consumed immediately.
- */
- public static final int CONTENT_STATUS_READY = 0;
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is pending, waiting on either a download or purchase operation to complete
- * before it can be consumed.
- */
- public static final int CONTENT_STATUS_PENDING = 1;
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is available, but needs to be first purchased, rented, subscribed or
- * downloaded before it can be consumed.
- */
- public static final int CONTENT_STATUS_AVAILABLE = 2;
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is not available. This could be content not available in a certain region or
- * incompatible with the device in use.
- */
- public static final int CONTENT_STATUS_UNAVAILABLE = 3;
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content referred by
- * the notification is suitable for all audiences.
- */
- public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content
- * referred by the notification is suitable for audiences of low maturity and above.
- */
- public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content
- * referred by the notification is suitable for audiences of medium maturity and above.
- */
- public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content
- * referred by the notification is suitable for audiences of high maturity and above.
- */
- public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
-
- private String[] mTypes;
- private String[] mGenres;
- private String mPricingType;
- private String mPricingValue;
- private int mContentStatus = -1;
- private String mMaturityRating;
- private long mRunLength = -1;
-
- /**
- * Create a {@link ContentInfoExtender} with default options.
- */
- public ContentInfoExtender() {
- }
-
- /**
- * Create a {@link ContentInfoExtender} from the ContentInfoExtender options of an existing
- * Notification.
- *
- * @param notif The notification from which to copy options.
- */
- public ContentInfoExtender(Notification notif) {
- Bundle contentBundle = notif.extras == null ?
- null : notif.extras.getBundle(EXTRA_CONTENT_INFO_EXTENDER);
- if (contentBundle != null) {
- mTypes = contentBundle.getStringArray(KEY_CONTENT_TYPE);
- mGenres = contentBundle.getStringArray(KEY_CONTENT_GENRES);
- mPricingType = contentBundle.getString(KEY_CONTENT_PRICING_TYPE);
- mPricingValue = contentBundle.getString(KEY_CONTENT_PRICING_VALUE);
- mContentStatus = contentBundle.getInt(KEY_CONTENT_STATUS, -1);
- mMaturityRating = contentBundle.getString(KEY_CONTENT_MATURITY_RATING);
- mRunLength = contentBundle.getLong(KEY_CONTENT_RUN_LENGTH, -1);
- }
- }
-
- /**
- * Apply content extensions to a notification that is being built. This is typically called
- * by the {@link Notification.Builder#extend(Notification.Extender)} method of
- * {@link Notification.Builder}.
- */
- @Override
- public Notification.Builder extend(Notification.Builder builder) {
- Bundle contentBundle = new Bundle();
-
- if (mTypes != null) {
- contentBundle.putStringArray(KEY_CONTENT_TYPE, mTypes);
- }
- if (mGenres != null) {
- contentBundle.putStringArray(KEY_CONTENT_GENRES, mGenres);
- }
- if (mPricingType != null) {
- contentBundle.putString(KEY_CONTENT_PRICING_TYPE, mPricingType);
- }
- if (mPricingValue != null) {
- contentBundle.putString(KEY_CONTENT_PRICING_VALUE, mPricingValue);
- }
- if (mContentStatus != -1) {
- contentBundle.putInt(KEY_CONTENT_STATUS, mContentStatus);
- }
- if (mMaturityRating != null) {
- contentBundle.putString(KEY_CONTENT_MATURITY_RATING, mMaturityRating);
- }
- if (mRunLength > 0) {
- contentBundle.putLong(KEY_CONTENT_RUN_LENGTH, mRunLength);
- }
-
- builder.getExtras().putBundle(EXTRA_CONTENT_INFO_EXTENDER, contentBundle);
- return builder;
- }
-
- /**
- * Sets the content types associated with the notification content. The first tag entry will
- * be considered the primary type for the content and will be used for ranking purposes.
- * Other secondary type tags may be provided, if applicable, and may be used for filtering
- * purposes.
- *
- * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code>
- * constants) that describe the content referred to by a notification.
- */
- public ContentInfoExtender setContentTypes(String[] types) {
- mTypes = types;
- return this;
- }
-
- /**
- * Returns an array containing the content types that describe the content associated with
- * the notification. The first tag entry is considered the primary type for the content, and
- * is used for content ranking purposes.
- *
- * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants)
- * that describe the content associated with the notification.
- * @see ContentInfoExtender#setContentTypes
- */
- public String[] getContentTypes() {
- return mTypes;
- }
-
- /**
- * Returns the primary content type tag for the content associated with the notification.
- *
- * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating
- * the primary type for the content associated with the notification.
- * @see ContentInfoExtender#setContentTypes
- */
- public String getPrimaryContentType() {
- if (mTypes == null || mTypes.length == 0) {
- return null;
- }
- return mTypes[0];
- }
-
- /**
- * Sets the content genres associated with the notification content. These genres may be
- * used for content ranking. Genres are open ended String tags.
- * <p>
- * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
- *
- * @param genres Array of genre string tags that describe the content referred to by a
- * notification.
- */
- public ContentInfoExtender setGenres(String[] genres) {
- mGenres = genres;
- return this;
- }
-
- /**
- * Returns an array containing the content genres that describe the content associated with
- * the notification.
- *
- * @return An array of genre tags that describe the content associated with the
- * notification.
- * @see ContentInfoExtender#setGenres
- */
- public String[] getGenres() {
- return mGenres;
- }
-
- /**
- * Sets the pricing and availability information for the content associated with the
- * notification. The provided information will indicate the access model for the content
- * (free, rental, purchase or subscription) and the price value (if not free).
- *
- * @param priceType Pricing type for this content. Must be one of the predefined pricing
- * type tags (see the <code>CONTENT_PRICING_*</code> constants).
- * @param priceValue A string containing a representation of the content price in the
- * current locale and currency.
- * @return This object for method chaining.
- */
- public ContentInfoExtender setPricingInformation(String priceType, String priceValue) {
- mPricingType = priceType;
- mPricingValue = priceValue;
- return this;
- }
-
- /**
- * Gets the pricing type for the content associated with the notification.
- *
- * @return A predefined tag indicating the pricing type for the content (see the <code>
- * CONTENT_PRICING_*</code> constants).
- * @see ContentInfoExtender#setPricingInformation
- */
- public String getPricingType() {
- return mPricingType;
- }
-
- /**
- * Gets the price value (when applicable) for the content associated with a notification.
- * The value will be provided as a String containing the price in the appropriate currency
- * for the current locale.
- *
- * @return A string containing a representation of the content price in the current locale
- * and currency.
- * @see ContentInfoExtender#setPricingInformation
- */
- public String getPricingValue() {
- if (mPricingType == null || CONTENT_PRICING_FREE.equals(mPricingType)) {
- return null;
- }
- return mPricingValue;
- }
-
- /**
- * Sets the availability status for the content associated with the notification. This
- * status indicates whether the referred content is ready to be consumed on the device, or
- * if the user must first purchase, rent, subscribe to, or download the content.
- *
- * @param contentStatus The status value for this content. Must be one of the predefined
- * content status values (see the <code>CONTENT_STATUS_*</code> constants).
- */
- public ContentInfoExtender setStatus(int contentStatus) {
- mContentStatus = contentStatus;
- return this;
- }
-
- /**
- * Returns status value for the content associated with the notification. This status
- * indicates whether the referred content is ready to be consumed on the device, or if the
- * user must first purchase, rent, subscribe to, or download the content.
- *
- * @return The status value for this content, or -1 is a valid status has not been specified
- * (see the <code>CONTENT_STATUS_*</code> for the defined valid status values).
- * @see ContentInfoExtender#setStatus
- */
- public int getStatus() {
- return mContentStatus;
- }
-
- /**
- * Sets the maturity level rating for the content associated with the notification.
- *
- * @param maturityRating A tag indicating the maturity level rating for the content. This
- * tag must be one of the predefined maturity rating tags (see the <code>
- * CONTENT_MATURITY_*</code> constants).
- */
- public ContentInfoExtender setMaturityRating(String maturityRating) {
- mMaturityRating = maturityRating;
- return this;
- }
-
- /**
- * Returns the maturity level rating for the content associated with the notification.
- *
- * @return returns a predefined tag indicating the maturity level rating for the content
- * (see the <code> CONTENT_MATURITY_*</code> constants).
- * @see ContentInfoExtender#setMaturityRating
- */
- public String getMaturityRating() {
- return mMaturityRating;
- }
-
- /**
- * Sets the running time (when applicable) for the content associated with the notification.
- *
- * @param length The runing time, in seconds, of the content associated with the
- * notification.
- */
- public ContentInfoExtender setRunningTime(long length) {
- mRunLength = length;
- return this;
- }
-
- /**
- * Returns the running time for the content associated with the notification.
- *
- * @return The running time, in seconds, of the content associated with the notification.
- * @see ContentInfoExtender#setRunningTime
- */
- public long getRunningTime() {
- return mRunLength;
- }
- }
-
- /**
* Get an array of Notification objects from a parcelable array bundle field.
* Update the bundle to have a typed array so fetches in the future don't need
* to do an array copy.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index d8556a2..6fca0de 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -33,15 +33,21 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import android.util.ArraySet;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.HashSet;
+import java.util.Collection;
import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import org.xmlpull.v1.XmlPullParserException;
+
/**
* Provides the central interface between an
* application and Android's data backup infrastructure. An application that wishes
@@ -164,7 +170,6 @@ public abstract class BackupAgent extends ContextWrapper {
* to do one-time initialization before the actual backup or restore operation
* is begun.
* <p>
- * Agents do not need to override this method.
*/
public void onCreate() {
}
@@ -268,19 +273,41 @@ public abstract class BackupAgent extends ContextWrapper {
* listed above. Apps only need to override this method if they need to impose special
* limitations on which files are being stored beyond the control that
* {@link #getNoBackupFilesDir()} offers.
+ * Alternatively they can provide an xml resource to specify what data to include or exclude.
+ *
*
* @param data A structured wrapper pointing to the backup destination.
* @throws IOException
*
* @see Context#getNoBackupFilesDir()
+ * @see ApplicationInfo#fullBackupContent
* @see #fullBackupFile(File, FullBackupDataOutput)
* @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
*/
public void onFullBackup(FullBackupDataOutput data) throws IOException {
- ApplicationInfo appInfo = getApplicationInfo();
+ FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
+ if (!backupScheme.isFullBackupContentEnabled()) {
+ return;
+ }
+
+ Map<String, Set<String>> manifestIncludeMap;
+ ArraySet<String> manifestExcludeSet;
+ try {
+ manifestIncludeMap =
+ backupScheme.maybeParseAndGetCanonicalIncludePaths();
+ manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
+ } catch (IOException | XmlPullParserException e) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "Exception trying to parse fullBackupContent xml file!"
+ + " Aborting full backup.", e);
+ }
+ return;
+ }
+
+ final String packageName = getPackageName();
+ final ApplicationInfo appInfo = getApplicationInfo();
- // Note that we don't need to think about the no_backup dir because it's outside
- // all of the ones we will be traversing
String rootDir = new File(appInfo.dataDir).getCanonicalPath();
String filesDir = getFilesDir().getCanonicalPath();
String nobackupDir = getNoBackupFilesDir().getCanonicalPath();
@@ -292,34 +319,49 @@ public abstract class BackupAgent extends ContextWrapper {
? new File(appInfo.nativeLibraryDir).getCanonicalPath()
: null;
- // Filters, the scan queue, and the set of resulting entities
- HashSet<String> filterSet = new HashSet<String>();
- String packageName = getPackageName();
+ // Maintain a set of excluded directories so that as we traverse the tree we know we're not
+ // going places we don't expect, and so the manifest includes can't take precedence over
+ // what the framework decides is not to be included.
+ final ArraySet<String> traversalExcludeSet = new ArraySet<String>();
- // Okay, start with the app's root tree, but exclude all of the canonical subdirs
+ // Add the directories we always exclude.
+ traversalExcludeSet.add(cacheDir);
+ traversalExcludeSet.add(codeCacheDir);
+ traversalExcludeSet.add(nobackupDir);
if (libDir != null) {
- filterSet.add(libDir);
+ traversalExcludeSet.add(libDir);
}
- filterSet.add(cacheDir);
- filterSet.add(codeCacheDir);
- filterSet.add(databaseDir);
- filterSet.add(sharedPrefsDir);
- filterSet.add(filesDir);
- filterSet.add(nobackupDir);
- fullBackupFileTree(packageName, FullBackup.ROOT_TREE_TOKEN, rootDir, filterSet, data);
-
- // Now do the same for the files dir, db dir, and shared prefs dir
- filterSet.add(rootDir);
- filterSet.remove(filesDir);
- fullBackupFileTree(packageName, FullBackup.DATA_TREE_TOKEN, filesDir, filterSet, data);
-
- filterSet.add(filesDir);
- filterSet.remove(databaseDir);
- fullBackupFileTree(packageName, FullBackup.DATABASE_TREE_TOKEN, databaseDir, filterSet, data);
-
- filterSet.add(databaseDir);
- filterSet.remove(sharedPrefsDir);
- fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data);
+
+ traversalExcludeSet.add(databaseDir);
+ traversalExcludeSet.add(sharedPrefsDir);
+ traversalExcludeSet.add(filesDir);
+
+ // Root dir first.
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(rootDir);
+
+ // Data dir next.
+ traversalExcludeSet.remove(filesDir);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.DATA_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(filesDir);
+
+ // Database directory.
+ traversalExcludeSet.remove(databaseDir);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.DATABASE_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(databaseDir);
+
+ // SharedPrefs.
+ traversalExcludeSet.remove(sharedPrefsDir);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(sharedPrefsDir);
// getExternalFilesDir() location associated with this app. Technically there should
// not be any files here if the app does not properly have permission to access
@@ -331,8 +373,36 @@ public abstract class BackupAgent extends ContextWrapper {
if (Process.myUid() != Process.SYSTEM_UID) {
File efLocation = getExternalFilesDir(null);
if (efLocation != null) {
- fullBackupFileTree(packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN,
- efLocation.getCanonicalPath(), null, data);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ }
+
+ }
+ }
+
+ /**
+ * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
+ * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
+ * is a directory.
+ */
+ private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
+ Map<String, Set<String>> includeMap,
+ ArraySet<String> filterSet,
+ ArraySet<String> traversalExcludeSet,
+ FullBackupDataOutput data)
+ throws IOException {
+ if (includeMap == null || includeMap.size() == 0) {
+ // Do entire sub-tree for the provided token.
+ fullBackupFileTree(packageName, domainToken,
+ FullBackup.getBackupScheme(this).tokenToDirectoryPath(domainToken),
+ filterSet, traversalExcludeSet, data);
+ } else if (includeMap.get(domainToken) != null) {
+ // This will be null if the xml parsing didn't yield any rules for
+ // this domain (there may still be rules for other domains).
+ for (String includeFile : includeMap.get(domainToken)) {
+ fullBackupFileTree(packageName, domainToken, includeFile, filterSet,
+ traversalExcludeSet, data);
}
}
}
@@ -430,21 +500,31 @@ public abstract class BackupAgent extends ContextWrapper {
// without transmitting any file data.
if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ " rootpath=" + rootpath);
-
+
FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
}
/**
* Scan the dir tree (if it actually exists) and process each entry we find. If the
- * 'excludes' parameter is non-null, it is consulted each time a new file system entity
+ * 'excludes' parameters are non-null, they are consulted each time a new file system entity
* is visited to see whether that entity (and its subtree, if appropriate) should be
* omitted from the backup process.
*
+ * @param systemExcludes An optional list of excludes.
* @hide
*/
- protected final void fullBackupFileTree(String packageName, String domain, String rootPath,
- HashSet<String> excludes, FullBackupDataOutput output) {
- File rootFile = new File(rootPath);
+ protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
+ ArraySet<String> manifestExcludes,
+ ArraySet<String> systemExcludes,
+ FullBackupDataOutput output) {
+ // Pull out the domain and set it aside to use when making the tarball.
+ String domainPath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
+ if (domainPath == null) {
+ // Should never happen.
+ return;
+ }
+
+ File rootFile = new File(startingPath);
if (rootFile.exists()) {
LinkedList<File> scanQueue = new LinkedList<File>();
scanQueue.add(rootFile);
@@ -456,7 +536,10 @@ public abstract class BackupAgent extends ContextWrapper {
filePath = file.getCanonicalPath();
// prune this subtree?
- if (excludes != null && excludes.contains(filePath)) {
+ if (manifestExcludes != null && manifestExcludes.contains(filePath)) {
+ continue;
+ }
+ if (systemExcludes != null && systemExcludes.contains(filePath)) {
continue;
}
@@ -475,14 +558,20 @@ public abstract class BackupAgent extends ContextWrapper {
}
} catch (IOException e) {
if (DEBUG) Log.w(TAG, "Error canonicalizing path of " + file);
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "Error canonicalizing path of " + file);
+ }
continue;
} catch (ErrnoException e) {
if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "Error scanning file " + file + " : " + e);
+ }
continue;
}
// Finally, back this file up (or measure it) before proceeding
- FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, output);
+ FullBackup.backupToTar(packageName, domain, null, domainPath, filePath, output);
}
}
}
@@ -516,10 +605,91 @@ public abstract class BackupAgent extends ContextWrapper {
public void onRestoreFile(ParcelFileDescriptor data, long size,
File destination, int type, long mode, long mtime)
throws IOException {
+ FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this);
+ if (!bs.isFullBackupContentEnabled()) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile \"" + destination.getCanonicalPath()
+ + "\" : fullBackupContent not enabled for " + getPackageName());
+ }
+ return;
+ }
+ Map<String, Set<String>> includes = null;
+ ArraySet<String> excludes = null;
+ final String destinationCanonicalPath = destination.getCanonicalPath();
+ try {
+ includes = bs.maybeParseAndGetCanonicalIncludePaths();
+ excludes = bs.maybeParseAndGetCanonicalExcludePaths();
+ } catch (XmlPullParserException e) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile \"" + destinationCanonicalPath
+ + "\" : Exception trying to parse fullBackupContent xml file!"
+ + " Aborting onRestoreFile.", e);
+ }
+ return;
+ }
+
+ if (excludes != null &&
+ isFileSpecifiedInPathList(destination, excludes)) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile: \"" + destinationCanonicalPath + "\": listed in"
+ + " excludes; skipping.");
+ }
+ return;
+ }
+
+ if (includes != null && !includes.isEmpty()) {
+ // Rather than figure out the <include/> domain based on the path (a lot of code, and
+ // it's a small list), we'll go through and look for it.
+ boolean explicitlyIncluded = false;
+ for (Set<String> domainIncludes : includes.values()) {
+ explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes);
+ if (explicitlyIncluded) {
+ break;
+ }
+ }
+ if (!explicitlyIncluded) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile: Trying to restore \""
+ + destinationCanonicalPath + "\" but it isn't specified"
+ + " in the included files; skipping.");
+ }
+ return;
+ }
+ }
FullBackup.restoreFile(data, size, type, mode, mtime, destination);
}
/**
+ * @return True if the provided file is either directly in the provided list, or the provided
+ * file is within a directory in the list.
+ */
+ private boolean isFileSpecifiedInPathList(File file, Collection<String> canonicalPathList)
+ throws IOException {
+ for (String canonicalPath : canonicalPathList) {
+ File fileFromList = new File(canonicalPath);
+ if (fileFromList.isDirectory()) {
+ if (file.isDirectory()) {
+ // If they are both directories check exact equals.
+ return file.equals(fileFromList);
+ } else {
+ // O/w we have to check if the file is within the directory from the list.
+ return file.getCanonicalPath().startsWith(canonicalPath);
+ }
+ } else {
+ if (file.equals(fileFromList)) {
+ // Need to check the explicit "equals" so we don't end up with substrings.
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Only specialized platform agents should overload this entry point to support
* restores to crazy non-app locations.
* @hide
@@ -533,31 +703,9 @@ public abstract class BackupAgent extends ContextWrapper {
+ " domain=" + domain + " relpath=" + path + " mode=" + mode
+ " mtime=" + mtime);
- // Parse out the semantic domains into the correct physical location
- if (domain.equals(FullBackup.DATA_TREE_TOKEN)) {
- basePath = getFilesDir().getCanonicalPath();
- } else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) {
- basePath = getDatabasePath("foo").getParentFile().getCanonicalPath();
- } else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) {
- basePath = new File(getApplicationInfo().dataDir).getCanonicalPath();
- } else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) {
- basePath = getSharedPrefsFile("foo").getParentFile().getCanonicalPath();
- } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
- basePath = getCacheDir().getCanonicalPath();
- } else if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
- // make sure we can try to restore here before proceeding
- if (Process.myUid() != Process.SYSTEM_UID) {
- File efLocation = getExternalFilesDir(null);
- if (efLocation != null) {
- basePath = getExternalFilesDir(null).getCanonicalPath();
- mode = -1; // < 0 is a token to skip attempting a chmod()
- }
- }
- } else if (domain.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) {
- basePath = getNoBackupFilesDir().getCanonicalPath();
- } else {
- // Not a supported location
- Log.i(TAG, "Unrecognized domain " + domain);
+ basePath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
+ if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
+ mode = -1; // < 0 is a token to skip attempting a chmod()
}
// Now that we've figured out where the data goes, send it on its way
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 259884e..7718a36 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -16,16 +16,31 @@
package android.app.backup;
-import android.os.ParcelFileDescriptor;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
+import android.os.*;
+import android.os.Process;
import android.system.ErrnoException;
import android.system.Os;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.xmlpull.v1.XmlPullParserException;
/**
* Global constant definitions et cetera related to the full-backup-to-fd
* binary format. Nothing in this namespace is part of any API; it's all
@@ -35,6 +50,8 @@ import java.io.IOException;
*/
public class FullBackup {
static final String TAG = "FullBackup";
+ /** Enable this log tag to get verbose information while parsing the client xml. */
+ static final String TAG_XML_PARSER = "BackupXmlParserLogging";
public static final String APK_TREE_TOKEN = "a";
public static final String OBB_TREE_TOKEN = "obb";
@@ -60,6 +77,27 @@ public class FullBackup {
static public native int backupToTar(String packageName, String domain,
String linkdomain, String rootpath, String path, FullBackupDataOutput output);
+ private static final Map<String, BackupScheme> kPackageBackupSchemeMap =
+ new ArrayMap<String, BackupScheme>();
+
+ static synchronized BackupScheme getBackupScheme(Context context) {
+ BackupScheme backupSchemeForPackage =
+ kPackageBackupSchemeMap.get(context.getPackageName());
+ if (backupSchemeForPackage == null) {
+ backupSchemeForPackage = new BackupScheme(context);
+ kPackageBackupSchemeMap.put(context.getPackageName(), backupSchemeForPackage);
+ }
+ return backupSchemeForPackage;
+ }
+
+ public static BackupScheme getBackupSchemeForTest(Context context) {
+ BackupScheme testing = new BackupScheme(context);
+ testing.mExcludes = new ArraySet();
+ testing.mIncludes = new ArrayMap();
+ return testing;
+ }
+
+
/**
* Copy data from a socket to the given File location on permanent storage. The
* modification time and access mode of the resulting file will be set if desired,
@@ -106,6 +144,8 @@ public class FullBackup {
if (!parent.exists()) {
// in practice this will only be for the default semantic directories,
// and using the default mode for those is appropriate.
+ // This can also happen for the case where a parent directory has been
+ // excluded, but a file within that directory has been included.
parent.mkdirs();
}
out = new FileOutputStream(outFile);
@@ -154,4 +194,363 @@ public class FullBackup {
outFile.setLastModified(mtime);
}
}
+
+ @VisibleForTesting
+ public static class BackupScheme {
+ private final File FILES_DIR;
+ private final File DATABASE_DIR;
+ private final File ROOT_DIR;
+ private final File SHAREDPREF_DIR;
+ private final File EXTERNAL_DIR;
+ private final File CACHE_DIR;
+ private final File NOBACKUP_DIR;
+
+ final int mFullBackupContent;
+ final PackageManager mPackageManager;
+ final String mPackageName;
+
+ /**
+ * Parse out the semantic domains into the correct physical location.
+ */
+ String tokenToDirectoryPath(String domainToken) {
+ try {
+ if (domainToken.equals(FullBackup.DATA_TREE_TOKEN)) {
+ return FILES_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.DATABASE_TREE_TOKEN)) {
+ return DATABASE_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.ROOT_TREE_TOKEN)) {
+ return ROOT_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) {
+ return SHAREDPREF_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.CACHE_TREE_TOKEN)) {
+ return CACHE_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
+ if (EXTERNAL_DIR != null) {
+ return EXTERNAL_DIR.getCanonicalPath();
+ } else {
+ return null;
+ }
+ } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) {
+ return NOBACKUP_DIR.getCanonicalPath();
+ }
+ // Not a supported location
+ Log.i(TAG, "Unrecognized domain " + domainToken);
+ return null;
+ } catch (IOException e) {
+ Log.i(TAG, "Error reading directory for domain: " + domainToken);
+ return null;
+ }
+
+ }
+ /**
+ * A map of domain -> list of canonical file names in that domain that are to be included.
+ * We keep track of the domain so that we can go through the file system in order later on.
+ */
+ Map<String, Set<String>> mIncludes;
+ /**e
+ * List that will be populated with the canonical names of each file or directory that is
+ * to be excluded.
+ */
+ ArraySet<String> mExcludes;
+
+ BackupScheme(Context context) {
+ mFullBackupContent = context.getApplicationInfo().fullBackupContent;
+ mPackageManager = context.getPackageManager();
+ mPackageName = context.getPackageName();
+ FILES_DIR = context.getFilesDir();
+ DATABASE_DIR = context.getDatabasePath("foo").getParentFile();
+ ROOT_DIR = new File(context.getApplicationInfo().dataDir);
+ SHAREDPREF_DIR = context.getSharedPrefsFile("foo").getParentFile();
+ CACHE_DIR = context.getCacheDir();
+ NOBACKUP_DIR = context.getNoBackupFilesDir();
+ if (android.os.Process.myUid() != Process.SYSTEM_UID) {
+ EXTERNAL_DIR = context.getExternalFilesDir(null);
+ } else {
+ EXTERNAL_DIR = null;
+ }
+ }
+
+ boolean isFullBackupContentEnabled() {
+ if (mFullBackupContent < 0) {
+ // android:fullBackupContent="false", bail.
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"false\"");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return A mapping of domain -> canonical paths within that domain. Each of these paths
+ * specifies a file that the client has explicitly included in their backup set. If this
+ * map is empty we will back up the entire data directory (including managed external
+ * storage).
+ */
+ public synchronized Map<String, Set<String>> maybeParseAndGetCanonicalIncludePaths()
+ throws IOException, XmlPullParserException {
+ if (mIncludes == null) {
+ maybeParseBackupSchemeLocked();
+ }
+ return mIncludes;
+ }
+
+ /**
+ * @return A set of canonical paths that are to be excluded from the backup/restore set.
+ */
+ public synchronized ArraySet<String> maybeParseAndGetCanonicalExcludePaths()
+ throws IOException, XmlPullParserException {
+ if (mExcludes == null) {
+ maybeParseBackupSchemeLocked();
+ }
+ return mExcludes;
+ }
+
+ private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException {
+ // This not being null is how we know that we've tried to parse the xml already.
+ mIncludes = new ArrayMap<String, Set<String>>();
+ mExcludes = new ArraySet<String>();
+
+ if (mFullBackupContent == 0) {
+ // android:fullBackupContent="true" which means that we'll do everything.
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\"");
+ }
+ } else {
+ // android:fullBackupContent="@xml/some_resource".
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "android:fullBackupContent - found xml resource");
+ }
+ XmlResourceParser parser = null;
+ try {
+ parser = mPackageManager
+ .getResourcesForApplication(mPackageName)
+ .getXml(mFullBackupContent);
+ parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Throw it as an IOException
+ throw new IOException(e);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
+ Set<String> excludes,
+ Map<String, Set<String>> includes)
+ throws IOException, XmlPullParserException {
+ int event = parser.getEventType(); // START_DOCUMENT
+ while (event != XmlPullParser.START_TAG) {
+ event = parser.next();
+ }
+
+ if (!"full-backup-content".equals(parser.getName())) {
+ throw new XmlPullParserException("Xml file didn't start with correct tag" +
+ " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+ }
+
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "\n");
+ Log.v(TAG_XML_PARSER, "====================================================");
+ Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+ Log.v(TAG_XML_PARSER, "====================================================");
+ Log.v(TAG_XML_PARSER, "");
+ }
+
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ switch (event) {
+ case XmlPullParser.START_TAG:
+ validateInnerTagContents(parser);
+ final String domainFromXml = parser.getAttributeValue(null, "domain");
+ final File domainDirectory =
+ getDirectoryForCriteriaDomain(domainFromXml);
+ if (domainDirectory == null) {
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...parsing \"" + parser.getName() + "\": "
+ + "domain=\"" + domainFromXml + "\" invalid; skipping");
+ }
+ break;
+ }
+ final File canonicalFile =
+ extractCanonicalFile(domainDirectory,
+ parser.getAttributeValue(null, "path"));
+ if (canonicalFile == null) {
+ break;
+ }
+
+ Set<String> activeSet = parseCurrentTagForDomain(
+ parser, excludes, includes, domainFromXml);
+ activeSet.add(canonicalFile.getCanonicalPath());
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...parsed " + canonicalFile.getCanonicalPath()
+ + " for domain \"" + domainFromXml + "\"");
+ }
+
+ // Special case journal files (not dirs) for sqlite database. frowny-face.
+ // Note that for a restore, the file is never a directory (b/c it doesn't
+ // exist). We have no way of knowing a priori whether or not to expect a
+ // dir, so we add the -journal anyway to be safe.
+ if ("database".equals(domainFromXml) && !canonicalFile.isDirectory()) {
+ final String canonicalJournalPath =
+ canonicalFile.getCanonicalPath() + "-journal";
+ activeSet.add(canonicalJournalPath);
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...automatically generated "
+ + canonicalJournalPath + ". Ignore if nonexistant.");
+ }
+ }
+ }
+ }
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "\n");
+ Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
+ Log.v(TAG_XML_PARSER, "Final tally.");
+ Log.v(TAG_XML_PARSER, "Includes:");
+ if (includes.isEmpty()) {
+ Log.v(TAG_XML_PARSER, " ...nothing specified (This means the entirety of app"
+ + " data minus excludes)");
+ } else {
+ for (Map.Entry<String, Set<String>> entry : includes.entrySet()) {
+ Log.v(TAG_XML_PARSER, " domain=" + entry.getKey());
+ for (String includeData : entry.getValue()) {
+ Log.v(TAG_XML_PARSER, " " + includeData);
+ }
+ }
+ }
+
+ Log.v(TAG_XML_PARSER, "Excludes:");
+ if (excludes.isEmpty()) {
+ Log.v(TAG_XML_PARSER, " ...nothing to exclude.");
+ } else {
+ for (String excludeData : excludes) {
+ Log.v(TAG_XML_PARSER, " " + excludeData);
+ }
+ }
+
+ Log.v(TAG_XML_PARSER, " ");
+ Log.v(TAG_XML_PARSER, "====================================================");
+ Log.v(TAG_XML_PARSER, "\n");
+ }
+ }
+
+ private Set<String> parseCurrentTagForDomain(XmlPullParser parser,
+ Set<String> excludes,
+ Map<String, Set<String>> includes,
+ String domain)
+ throws XmlPullParserException {
+ if ("include".equals(parser.getName())) {
+ final String domainToken = getTokenForXmlDomain(domain);
+ Set<String> includeSet = includes.get(domainToken);
+ if (includeSet == null) {
+ includeSet = new ArraySet<String>();
+ includes.put(domainToken, includeSet);
+ }
+ return includeSet;
+ } else if ("exclude".equals(parser.getName())) {
+ return excludes;
+ } else {
+ // Unrecognised tag => hard failure.
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "Invalid tag found in xml \""
+ + parser.getName() + "\"; aborting operation.");
+ }
+ throw new XmlPullParserException("Unrecognised tag in backup" +
+ " criteria xml (" + parser.getName() + ")");
+ }
+ }
+
+ /**
+ * Map xml specified domain (human-readable, what clients put in their manifest's xml) to
+ * BackupAgent internal data token.
+ * @return null if the xml domain was invalid.
+ */
+ private String getTokenForXmlDomain(String xmlDomain) {
+ if ("root".equals(xmlDomain)) {
+ return FullBackup.ROOT_TREE_TOKEN;
+ } else if ("file".equals(xmlDomain)) {
+ return FullBackup.DATA_TREE_TOKEN;
+ } else if ("database".equals(xmlDomain)) {
+ return FullBackup.DATABASE_TREE_TOKEN;
+ } else if ("sharedpref".equals(xmlDomain)) {
+ return FullBackup.SHAREDPREFS_TREE_TOKEN;
+ } else if ("external".equals(xmlDomain)) {
+ return FullBackup.MANAGED_EXTERNAL_TREE_TOKEN;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ *
+ * @param domain Directory where the specified file should exist. Not null.
+ * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may be
+ * null.
+ * @return The canonical path of the file specified or null if no such file exists.
+ */
+ private File extractCanonicalFile(File domain, String filePathFromXml) {
+ if (filePathFromXml == null) {
+ // Allow things like <include domain="sharedpref"/>
+ filePathFromXml = "";
+ }
+ if (filePathFromXml.contains("..")) {
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...resolved \"" + domain.getPath() + " " + filePathFromXml
+ + "\", but the \"..\" path is not permitted; skipping.");
+ }
+ return null;
+ }
+ if (filePathFromXml.contains("//")) {
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...resolved \"" + domain.getPath() + " " + filePathFromXml
+ + "\", which contains the invalid \"//\" sequence; skipping.");
+ }
+ return null;
+ }
+ return new File(domain, filePathFromXml);
+ }
+
+ /**
+ * @param domain parsed from xml. Not sanitised before calling this function so may be null.
+ * @return The directory relevant to the domain specified.
+ */
+ private File getDirectoryForCriteriaDomain(String domain) {
+ if (TextUtils.isEmpty(domain)) {
+ return null;
+ }
+ if ("file".equals(domain)) {
+ return FILES_DIR;
+ } else if ("database".equals(domain)) {
+ return DATABASE_DIR;
+ } else if ("root".equals(domain)) {
+ return ROOT_DIR;
+ } else if ("sharedpref".equals(domain)) {
+ return SHAREDPREF_DIR;
+ } else if ("external".equals(domain)) {
+ return EXTERNAL_DIR;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Let's be strict about the type of xml the client can write. If we see anything untoward,
+ * throw an XmlPullParserException.
+ */
+ private void validateInnerTagContents(XmlPullParser parser)
+ throws XmlPullParserException {
+ if (parser.getAttributeCount() > 2) {
+ throw new XmlPullParserException("At most 2 tag attributes allowed for \""
+ + parser.getName() + "\" tag (\"domain\" & \"path\".");
+ }
+ if (!"include".equals(parser.getName()) && !"exclude".equals(parser.getName())) {
+ throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" +
+ " \"<exclude/>. You provided \"" + parser.getName() + "\"");
+ }
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9b4dcc6..0a77868 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1692,7 +1692,8 @@ public final class BluetoothAdapter {
* @param context Context of the application
* @param listener The service Listener for connection callbacks.
* @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH},
- * {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}.
+ * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
+ * {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
index baf2ade..128376f 100644
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ b/core/java/android/bluetooth/BluetoothHealthCallback.java
@@ -17,6 +17,7 @@
package android.bluetooth;
+import android.annotation.BinderThread;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -39,6 +40,7 @@ public abstract class BluetoothHealthCallback {
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
*/
+ @BinderThread
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
int status) {
Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
@@ -58,6 +60,7 @@ public abstract class BluetoothHealthCallback {
* @param channelId The id associated with the channel. This id will be used
* in future calls like when disconnecting the channel.
*/
+ @BinderThread
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
int channelId) {
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 7b4c6f9..014cb22 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -28,13 +28,41 @@ import android.os.IBinder;
import android.os.ServiceManager;
import android.util.Log;
-
+/**
+ * This class provides the APIs to control the Bluetooth SIM
+ * Access Profile (SAP).
+ *
+ * <p>BluetoothSap is a proxy object for controlling the Bluetooth
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothSap proxy object.
+ *
+ * <p>Each method is protected with its appropriate permission.
+ * @hide
+ */
public final class BluetoothSap implements BluetoothProfile {
private static final String TAG = "BluetoothSap";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ /**
+ * Intent used to broadcast the change in connection state of the profile.
+ *
+ * <p>This intent will have 4 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ * @hide
+ */
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
@@ -43,12 +71,22 @@ public final class BluetoothSap implements BluetoothProfile {
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- /** There was an error trying to obtain the state */
- public static final int STATE_ERROR = -1;
+ /**
+ * There was an error trying to obtain the state.
+ * @hide
+ */
+ public static final int STATE_ERROR = -1;
- public static final int RESULT_FAILURE = 0;
+ /**
+ * Connection state change succceeded.
+ * @hide
+ */
public static final int RESULT_SUCCESS = 1;
- /** Connection canceled before completion. */
+
+ /**
+ * Connection canceled before completion.
+ * @hide
+ */
public static final int RESULT_CANCELED = 2;
final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -124,6 +162,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Other public functions of BluetoothSap will return default error
* results once close() has been called. Multiple invocations of close()
* are ok.
+ * @hide
*/
public synchronized void close() {
IBluetoothManager mgr = mAdapter.getBluetoothManager();
@@ -152,6 +191,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get the current state of the BluetoothSap service.
* @return One of the STATE_ return codes, or STATE_ERROR if this proxy
* object is currently not connected to the Sap service.
+ * @hide
*/
public int getState() {
if (VDBG) log("getState()");
@@ -171,6 +211,7 @@ public final class BluetoothSap implements BluetoothProfile {
* @return The remote Bluetooth device, or null if not in connected or
* connecting state, or if this proxy object is not connected to
* the Sap service.
+ * @hide
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
@@ -189,6 +230,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Returns true if the specified Bluetooth device is connected.
* Returns false if not connected, or if this proxy object is not
* currently connected to the Sap service.
+ * @hide
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
@@ -206,6 +248,7 @@ public final class BluetoothSap implements BluetoothProfile {
/**
* Initiate connection. Initiation of outgoing connections is not
* supported for SAP server.
+ * @hide
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
@@ -218,6 +261,7 @@ public final class BluetoothSap implements BluetoothProfile {
* @param device Remote Bluetooth Device
* @return false on error,
* true otherwise
+ * @hide
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
@@ -238,6 +282,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get the list of connected devices. Currently at most one.
*
* @return list of connected devices
+ * @hide
*/
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
@@ -257,6 +302,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get the list of devices matching specified states. Currently at most one.
*
* @return list of matching devices
+ * @hide
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
@@ -276,6 +322,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get connection state of device
*
* @return device connection state
+ * @hide
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
@@ -300,6 +347,7 @@ public final class BluetoothSap implements BluetoothProfile {
* @param device Paired bluetooth device
* @param priority
* @return true if priority is set, false on error
+ * @hide
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
@@ -325,6 +373,7 @@ public final class BluetoothSap implements BluetoothProfile {
*
* @param device Bluetooth device
* @return priority of the device
+ * @hide
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 5702d11..5cf2300 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -91,9 +91,13 @@ public final class BluetoothSocket implements Closeable {
public static final int MAX_RFCOMM_CHANNEL = 30;
/*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
- /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
+ /** RFCOMM socket */
public static final int TYPE_RFCOMM = 1;
+
+ /** SCO socket */
public static final int TYPE_SCO = 2;
+
+ /** L2CAP socket */
public static final int TYPE_L2CAP = 3;
/*package*/ static final int EBADFD = 77;
@@ -578,8 +582,8 @@ public final class BluetoothSocket implements Closeable {
}
/**
- * Get the type of the underlying connection
- * @return one of TYPE_
+ * Get the type of the underlying connection.
+ * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
*/
public int getConnectionType() {
return mType;
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index 27b96bd..61b2e78 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -53,8 +53,10 @@ public abstract class ScanCallback {
/**
* Callback when a BLE advertisement has been found.
*
- * @param callbackType Determines how this callback was triggered. Could be of
- * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}
+ * @param callbackType Determines how this callback was triggered. Could be one of
+ * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES},
+ * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
+ * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST}
* @param result A Bluetooth LE scan result.
*/
public void onScanResult(int callbackType, ScanResult result) {
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index dad486d..4eeb577 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -59,17 +59,13 @@ public final class ScanSettings implements Parcelable {
/**
* A result callback is only triggered for the first advertisement packet received that matches
* the filter criteria.
- * @hide
*/
- @SystemApi
public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
/**
* Receive a callback when advertisements are no longer received from a device that has been
* previously reported by a first match callback.
- * @hide
*/
- @SystemApi
public static final int CALLBACK_TYPE_MATCH_LOST = 4;
@@ -78,21 +74,18 @@ public final class ScanSettings implements Parcelable {
*/
/**
* Match one advertisement per filter
- * @hide
*/
public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
/**
* Match few advertisement per filter, depends on current capability and availibility of
* the resources in hw
- * @hide
*/
public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
/**
* Match as many advertisement per filter as hw could allow, depends on current
* capability and availibility of the resources in hw
- * @hide
*/
public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
@@ -100,14 +93,12 @@ public final class ScanSettings implements Parcelable {
/**
* In Aggressive mode, hw will determine a match sooner even with feeble signal strength
* and few number of sightings/match in a duration.
- * @hide
*/
public static final int MATCH_MODE_AGGRESSIVE = 1;
/**
* For sticky mode, higher threshold of signal strength and sightings is required
* before reporting by hw
- * @hide
*/
public static final int MATCH_MODE_STICKY = 2;
@@ -324,7 +315,6 @@ public final class ScanSettings implements Parcelable {
* {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
* {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
* @throws IllegalArgumentException If the {@code matchMode} is invalid.
- * @hide
*/
public Builder setNumOfMatches(int numOfMatches) {
if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
@@ -342,7 +332,6 @@ public final class ScanSettings implements Parcelable {
* {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
* {@link ScanSettings#MATCH_MODE_STICKY}
* @throws IllegalArgumentException If the {@code matchMode} is invalid.
- * @hide
*/
public Builder setMatchMode(int matchMode) {
if (matchMode < MATCH_MODE_AGGRESSIVE
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6c32873..707ef30 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -96,6 +96,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public String backupAgentName;
/**
+ * An optional attribute that indicates the app supports automatic backup of app data.
+ * <p>0 is the default and means the app's entire data folder + managed external storage will
+ * be backed up;
+ * Any negative value indicates the app does not support full-data backup, though it may still
+ * want to participate via the traditional key/value backup API;
+ * A positive number specifies an xml resource in which the application has defined its backup
+ * include/exclude criteria.
+ * <p>If android:allowBackup is set to false, this attribute is ignored.
+ *
+ * @see {@link android.content.Context#getNoBackupFilesDir}
+ * @see {@link #FLAG_ALLOW_BACKUP}
+ */
+ public int fullBackupContent = 0;
+
+ /**
* The default extra UI options for activities in this application.
* Set from the {@link android.R.attr#uiOptions} attribute in the
* activity's manifest.
@@ -686,6 +701,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
}
pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
+ if (fullBackupContent > 0) {
+ pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
+ } else {
+ pw.println(prefix + "fullBackupContent=" + (fullBackupContent < 0 ? "false" : "true"));
+ }
super.dumpBack(pw, prefix);
}
@@ -763,6 +783,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
uiOptions = orig.uiOptions;
backupAgentName = orig.backupAgentName;
hardwareAccelerated = orig.hardwareAccelerated;
+ fullBackupContent = orig.fullBackupContent;
}
@@ -816,6 +837,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(descriptionRes);
dest.writeInt(uiOptions);
dest.writeInt(hardwareAccelerated ? 1 : 0);
+ dest.writeInt(fullBackupContent);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -868,6 +890,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
descriptionRes = source.readInt();
uiOptions = source.readInt();
hardwareAccelerated = source.readInt() != 0;
+ fullBackupContent = source.readInt();
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0b24594..94b0223 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -96,9 +96,9 @@ interface IPackageManager {
void removePermission(String name);
- boolean grantPermission(String packageName, String permissionName, int userId);
+ void grantPermission(String packageName, String permissionName, int userId);
- boolean revokePermission(String packageName, String permissionName, int userId);
+ void revokePermission(String packageName, String permissionName, int userId);
boolean isProtectedBroadcast(String actionName);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9596c42..acc27c3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2421,8 +2421,8 @@ public class PackageParser {
if (allowBackup) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
- // backupAgent, killAfterRestore, and restoreAnyVersion are only relevant
- // if backup is possible for the given application.
+ // backupAgent, killAfterRestore, fullBackupContent and restoreAnyVersion are only
+ // relevant if backup is possible for the given application.
String backupAgent = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent,
Configuration.NATIVE_CONFIG_VERSION);
@@ -2449,6 +2449,20 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
}
}
+
+ TypedValue v = sa.peekValue(
+ com.android.internal.R.styleable.AndroidManifestApplication_fullBackupContent);
+ if (v != null && (ai.fullBackupContent = v.resourceId) == 0) {
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "fullBackupContent specified as boolean=" +
+ (v.data == 0 ? "false" : "true"));
+ }
+ // "false" => -1, "true" => 0
+ ai.fullBackupContent = (v.data == 0 ? -1 : 0);
+ }
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "fullBackupContent=" + ai.fullBackupContent + " for " + pkgName);
+ }
}
TypedValue v = sa.peekValue(
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d88594d..1fc69c0 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -460,9 +460,8 @@ public class Camera {
mEventHandler = null;
}
- String packageName = ActivityThread.currentPackageName();
-
- return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
+ return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
+ ActivityThread.currentOpPackageName());
}
private int cameraInitNormal(int cameraId) {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 9bc2f46..7b96e20 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -38,13 +38,13 @@ interface ICameraService
int getCameraInfo(int cameraId, out CameraInfo info);
int connect(ICameraClient client, int cameraId,
- String clientPackageName,
+ String opPackageName,
int clientUid,
// Container for an ICamera object
out BinderHolder device);
int connectDevice(ICameraDeviceCallbacks callbacks, int cameraId,
- String clientPackageName,
+ String opPackageName,
int clientUid,
// Container for an ICameraDeviceUser object
out BinderHolder device);
@@ -69,7 +69,7 @@ interface ICameraService
int connectLegacy(ICameraClient client, int cameraId,
int halVersion,
- String clientPackageName,
+ String opPackageName,
int clientUid,
// Container for an ICamera object
out BinderHolder device);
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 11037fd..22a9e9c 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -41,16 +41,19 @@ import java.util.List;
*/
public class SystemSensorManager extends SensorManager {
private static native void nativeClassInit();
- private static native int nativeGetNextSensor(Sensor sensor, int next);
- private static native int nativeEnableDataInjection(boolean enable);
+ private static native long nativeCreate(String opPackageName);
+ private static native int nativeGetNextSensor(long nativeInstance, Sensor sensor, int next);
+ private static native int nativeEnableDataInjection(long nativeInstance, boolean enable);
private static boolean sSensorModuleInitialized = false;
- private static final Object sSensorModuleLock = new Object();
- private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
- private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
private static InjectEventQueue mInjectEventQueue = null;
private static boolean mDataInjectionMode = false;
+ private final Object mLock = new Object();
+
+ private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>();
+ private final SparseArray<Sensor> mHandleToSensor = new SparseArray<>();
+
// Listener list
private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
new HashMap<SensorEventListener, SensorEventQueue>();
@@ -60,44 +63,44 @@ public class SystemSensorManager extends SensorManager {
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
private final int mTargetSdkLevel;
- private final String mPackageName;
+ private final Context mContext;
private final boolean mHasDataInjectionPermissions;
+ private final long mNativeInstance;
/** {@hide} */
public SystemSensorManager(Context context, Looper mainLooper) {
mMainLooper = mainLooper;
mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
- mPackageName = context.getPackageName();
- synchronized(sSensorModuleLock) {
+ mContext = context;
+ mNativeInstance = nativeCreate(context.getOpPackageName());
+
+ synchronized(mLock) {
if (!sSensorModuleInitialized) {
sSensorModuleInitialized = true;
-
nativeClassInit();
-
- // initialize the sensor list
- final ArrayList<Sensor> fullList = sFullSensorsList;
- int i = 0;
- do {
- Sensor sensor = new Sensor();
- i = nativeGetNextSensor(sensor, i);
- if (i>=0) {
- //Log.d(TAG, "found sensor: " + sensor.getName() +
- // ", handle=" + sensor.getHandle());
- fullList.add(sensor);
- sHandleToSensor.append(sensor.getHandle(), sensor);
- }
- } while (i>0);
}
mHasDataInjectionPermissions = context.checkSelfPermission(
Manifest.permission.HARDWARE_TEST) == PackageManager.PERMISSION_GRANTED;
}
+
+ // initialize the sensor list
+ int i = 0;
+ while(true) {
+ Sensor sensor = new Sensor();
+ i = nativeGetNextSensor(mNativeInstance, sensor, i);
+ if (i <= 0) {
+ break;
+ }
+ mFullSensorsList.add(sensor);
+ mHandleToSensor.append(sensor.getHandle(), sensor);
+ }
}
/** @hide */
@Override
protected List<Sensor> getFullSensorList() {
- return sFullSensorsList;
+ return mFullSensorsList;
}
@@ -232,8 +235,8 @@ public class SystemSensorManager extends SensorManager {
throw new SecurityException("Permission denial. Calling enableDataInjection without "
+ Manifest.permission.HARDWARE_TEST);
}
- synchronized (sSensorModuleLock) {
- int ret = nativeEnableDataInjection(enable);
+ synchronized (mLock) {
+ int ret = nativeEnableDataInjection(mNativeInstance, enable);
// The HAL does not support injection. Ignore.
if (ret != 0) {
Log.e(TAG, "HAL does not support data injection");
@@ -255,7 +258,7 @@ public class SystemSensorManager extends SensorManager {
throw new SecurityException("Permission denial. Calling injectSensorData without "
+ Manifest.permission.HARDWARE_TEST);
}
- synchronized (sSensorModuleLock) {
+ synchronized (mLock) {
if (!mDataInjectionMode) {
Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
return false;
@@ -284,15 +287,17 @@ public class SystemSensorManager extends SensorManager {
* SensorManager instance.
*/
private static abstract class BaseEventQueue {
- private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak,
- MessageQueue msgQ, float[] scratch, String packageName, int mode);
+ private static native long nativeInitBaseEventQueue(long nativeManager,
+ WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, float[] scratch,
+ String packageName, int mode, String opPackageName);
private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
int maxBatchReportLatencyUs);
private static native int nativeDisableSensor(long eventQ, int handle);
private static native void nativeDestroySensorEventQueue(long eventQ);
private static native int nativeFlushSensor(long eventQ);
private static native int nativeInjectSensorData(long eventQ, int handle,
- float[] values,int accuracy, long timestamp);
+ float[] values,int accuracy, long timestamp);
+
private long nSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -305,8 +310,9 @@ public class SystemSensorManager extends SensorManager {
protected static final int OPERATING_MODE_DATA_INJECTION = 1;
BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) {
- nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this),
- looper.getQueue(), mScratch, manager.mPackageName, mode);
+ nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
+ new WeakReference<>(this), looper.getQueue(), mScratch,
+ manager.mContext.getPackageName(), mode, manager.mContext.getOpPackageName());
mCloseGuard.open("dispose");
mManager = manager;
}
@@ -339,7 +345,7 @@ public class SystemSensorManager extends SensorManager {
for (int i=0 ; i<mActiveSensors.size(); i++) {
if (mActiveSensors.valueAt(i) == true) {
int handle = mActiveSensors.keyAt(i);
- Sensor sensor = sHandleToSensor.get(handle);
+ Sensor sensor = mManager.mHandleToSensor.get(handle);
if (sensor != null) {
disableSensor(sensor);
mActiveSensors.put(handle, false);
@@ -452,7 +458,7 @@ public class SystemSensorManager extends SensorManager {
@Override
protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
long timestamp) {
- final Sensor sensor = sHandleToSensor.get(handle);
+ final Sensor sensor = mManager.mHandleToSensor.get(handle);
SensorEvent t = null;
synchronized (mSensorsEvents) {
t = mSensorsEvents.get(handle);
@@ -481,7 +487,7 @@ public class SystemSensorManager extends SensorManager {
@SuppressWarnings("unused")
protected void dispatchFlushCompleteEvent(int handle) {
if (mListener instanceof SensorEventListener2) {
- final Sensor sensor = sHandleToSensor.get(handle);
+ final Sensor sensor = mManager.mHandleToSensor.get(handle);
((SensorEventListener2)mListener).onFlushCompleted(sensor);
}
return;
@@ -519,7 +525,7 @@ public class SystemSensorManager extends SensorManager {
@Override
protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
long timestamp) {
- final Sensor sensor = sHandleToSensor.get(handle);
+ final Sensor sensor = mManager.mHandleToSensor.get(handle);
TriggerEvent t = null;
synchronized (mTriggerEvents) {
t = mTriggerEvents.get(handle);
@@ -546,7 +552,7 @@ public class SystemSensorManager extends SensorManager {
}
}
- static final class InjectEventQueue extends BaseEventQueue {
+ final class InjectEventQueue extends BaseEventQueue {
public InjectEventQueue(Looper looper, SystemSensorManager manager) {
super(looper, manager, OPERATING_MODE_DATA_INJECTION);
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index e2d2f61..9327f00 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -314,7 +314,7 @@ public final class CameraManager {
"Camera service is currently unavailable");
}
cameraService.connectDevice(callbacks, id,
- mContext.getPackageName(), USE_CALLING_UID, holder);
+ mContext.getOpPackageName(), USE_CALLING_UID, holder);
cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
} else {
// Use legacy camera implementation for HAL1 devices
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2d63e3f..d8c3361 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2571,10 +2571,8 @@ public class ConnectivityManager {
* method assumes that the caller has previously called {@link #registerNetworkCallback} to
* listen for network changes.
*
- * @param network{@link Network} specifying which network you're interested.
+ * @param network {@link Network} specifying which network you're interested.
* @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
- *
- * @hide
*/
public boolean requestBandwidthUpdate(Network network) {
try {
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 47e8e69..243ddf7 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -16,6 +16,9 @@
package android.os;
+import android.annotation.MainThread;
+import android.annotation.WorkerThread;
+
import java.util.ArrayDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
@@ -350,6 +353,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onPostExecute
* @see #publishProgress
*/
+ @WorkerThread
protected abstract Result doInBackground(Params... params);
/**
@@ -358,6 +362,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onPostExecute
* @see #doInBackground
*/
+ @MainThread
protected void onPreExecute() {
}
@@ -374,6 +379,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onCancelled(Object)
*/
@SuppressWarnings({"UnusedDeclaration"})
+ @MainThread
protected void onPostExecute(Result result) {
}
@@ -387,6 +393,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
+ @MainThread
protected void onProgressUpdate(Progress... values) {
}
@@ -405,6 +412,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #isCancelled()
*/
@SuppressWarnings({"UnusedParameters"})
+ @MainThread
protected void onCancelled(Result result) {
onCancelled();
}
@@ -421,6 +429,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #cancel(boolean)
* @see #isCancelled()
*/
+ @MainThread
protected void onCancelled() {
}
@@ -535,6 +544,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
* @see #execute(Runnable)
*/
+ @MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@@ -572,6 +582,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
*
* @see #execute(Object[])
*/
+ @MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
@@ -604,6 +615,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #execute(Object[])
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
*/
+ @MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
@@ -622,6 +634,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onProgressUpdate
* @see #doInBackground
*/
+ @WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 5e9b8c1..74699fd 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -211,8 +211,9 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
} else if (obj instanceof Parcelable[]) {
Parcelable[] array = (Parcelable[]) obj;
for (int n = array.length - 1; n >= 0; n--) {
- if ((array[n].describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ Parcelable p = array[n];
+ if (p != null && ((p.describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
fdFound = true;
break;
}
@@ -221,7 +222,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
SparseArray<? extends Parcelable> array =
(SparseArray<? extends Parcelable>) obj;
for (int n = array.size() - 1; n >= 0; n--) {
- if ((array.valueAt(n).describeContents()
+ Parcelable p = array.valueAt(n);
+ if (p != null && (p.describeContents()
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
fdFound = true;
break;
diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl
index 0cc1603..5e8590a 100644
--- a/core/java/android/os/IPermissionController.aidl
+++ b/core/java/android/os/IPermissionController.aidl
@@ -21,4 +21,5 @@ package android.os;
interface IPermissionController {
boolean checkPermission(String permission, int pid, int uid);
String[] getPackagesForUid(int uid);
+ boolean isRuntimePermission(String permission);
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 50e7d1c..3fdabee 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -584,7 +584,7 @@ public class StorageManager {
// Nickname always takes precedence when defined
if (!TextUtils.isEmpty(vol.fsUuid)) {
final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
- if (!TextUtils.isEmpty(rec.nickname)) {
+ if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
return rec.nickname;
}
}
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 918933b..3319e64 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -376,6 +376,7 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
Class clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name);
constructor = clazz.getConstructor(mConstructorSignature);
+ constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 396cf19..e07e846 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -9062,5 +9062,15 @@ public final class ContactsContract {
*/
public static final Uri CONTENT_URI = Uri.withAppendedPath(METADATA_AUTHORITY_URI,
"metadata_sync");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of contact metadata
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single contact metadata.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
}
}
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
index 0592a84..d7bf10c 100644
--- a/core/java/android/service/carrier/CarrierMessagingService.java
+++ b/core/java/android/service/carrier/CarrierMessagingService.java
@@ -80,6 +80,11 @@ public abstract class CarrierMessagingService extends Service {
*/
public static final int DOWNLOAD_STATUS_ERROR = 2;
+ /**
+ * Flag to request SMS delivery status report.
+ */
+ public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1;
+
private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
/**
@@ -103,12 +108,14 @@ public abstract class CarrierMessagingService extends Service {
/**
* Override this method to intercept text SMSs sent from the device.
+ * @deprecated Override {@link #onSendTextSms} below instead.
*
* @param text the text to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param callback result callback. Call with a {@link SendSmsResult}.
*/
+ @Deprecated
public void onSendTextSms(
@NonNull String text, int subId, @NonNull String destAddress,
@NonNull ResultCallback<SendSmsResult> callback) {
@@ -120,7 +127,25 @@ public abstract class CarrierMessagingService extends Service {
}
/**
+ * Override this method to intercept text SMSs sent from the device.
+ *
+ * @param text the text to send
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param callback result callback. Call with a {@link SendSmsResult}.
+ */
+ public void onSendTextSms(
+ @NonNull String text, int subId, @NonNull String destAddress,
+ int sendSmsFlag, @NonNull ResultCallback<SendSmsResult> callback) {
+ // optional
+ onSendTextSms(text, subId, destAddress, callback);
+ }
+
+ /**
* Override this method to intercept binary SMSs sent from the device.
+ * @deprecated Override {@link #onSendDataSms} below instead.
*
* @param data the binary content
* @param subId SMS subscription ID of the SIM
@@ -128,6 +153,7 @@ public abstract class CarrierMessagingService extends Service {
* @param destPort the destination port
* @param callback result callback. Call with a {@link SendSmsResult}.
*/
+ @Deprecated
public void onSendDataSms(@NonNull byte[] data, int subId,
@NonNull String destAddress, int destPort,
@NonNull ResultCallback<SendSmsResult> callback) {
@@ -139,13 +165,33 @@ public abstract class CarrierMessagingService extends Service {
}
/**
+ * Override this method to intercept binary SMSs sent from the device.
+ *
+ * @param data the binary content
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param destPort the destination port
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param callback result callback. Call with a {@link SendSmsResult}.
+ */
+ public void onSendDataSms(@NonNull byte[] data, int subId,
+ @NonNull String destAddress, int destPort, int sendSmsFlag,
+ @NonNull ResultCallback<SendSmsResult> callback) {
+ // optional
+ onSendDataSms(data, subId, destAddress, destPort, callback);
+ }
+
+ /**
* Override this method to intercept long SMSs sent from the device.
+ * @deprecated Override {@link #onSendMultipartTextSms} below instead.
*
* @param parts a {@link List} of the message parts
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param callback result callback. Call with a {@link SendMultipartSmsResult}.
*/
+ @Deprecated
public void onSendMultipartTextSms(@NonNull List<String> parts,
int subId, @NonNull String destAddress,
@NonNull ResultCallback<SendMultipartSmsResult> callback) {
@@ -158,6 +204,23 @@ public abstract class CarrierMessagingService extends Service {
}
/**
+ * Override this method to intercept long SMSs sent from the device.
+ *
+ * @param parts a {@link List} of the message parts
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param callback result callback. Call with a {@link SendMultipartSmsResult}.
+ */
+ public void onSendMultipartTextSms(@NonNull List<String> parts,
+ int subId, @NonNull String destAddress, int sendSmsFlag,
+ @NonNull ResultCallback<SendMultipartSmsResult> callback) {
+ // optional
+ onSendMultipartTextSms(parts, subId, destAddress, callback);
+ }
+
+ /**
* Override this method to intercept MMSs sent from the device.
*
* @param pduUri the content provider URI of the PDU to send
@@ -355,8 +418,9 @@ public abstract class CarrierMessagingService extends Service {
@Override
public void sendTextSms(String text, int subId, String destAddress,
- final ICarrierMessagingCallback callback) {
- onSendTextSms(text, subId, destAddress, new ResultCallback<SendSmsResult>() {
+ int sendSmsFlag, final ICarrierMessagingCallback callback) {
+ onSendTextSms(text, subId, destAddress, sendSmsFlag,
+ new ResultCallback<SendSmsResult>() {
@Override
public void onReceiveResult(final SendSmsResult result) throws RemoteException {
callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
@@ -366,8 +430,9 @@ public abstract class CarrierMessagingService extends Service {
@Override
public void sendDataSms(byte[] data, int subId, String destAddress, int destPort,
- final ICarrierMessagingCallback callback) {
- onSendDataSms(data, subId, destAddress, destPort, new ResultCallback<SendSmsResult>() {
+ int sendSmsFlag, final ICarrierMessagingCallback callback) {
+ onSendDataSms(data, subId, destAddress, destPort, sendSmsFlag,
+ new ResultCallback<SendSmsResult>() {
@Override
public void onReceiveResult(final SendSmsResult result) throws RemoteException {
callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
@@ -377,8 +442,8 @@ public abstract class CarrierMessagingService extends Service {
@Override
public void sendMultipartTextSms(List<String> parts, int subId, String destAddress,
- final ICarrierMessagingCallback callback) {
- onSendMultipartTextSms(parts, subId, destAddress,
+ int sendSmsFlag, final ICarrierMessagingCallback callback) {
+ onSendMultipartTextSms(parts, subId, destAddress, sendSmsFlag,
new ResultCallback<SendMultipartSmsResult>() {
@Override
public void onReceiveResult(final SendMultipartSmsResult result)
diff --git a/core/java/android/service/carrier/ICarrierMessagingService.aidl b/core/java/android/service/carrier/ICarrierMessagingService.aidl
index 40a9047..2d96c3d 100644
--- a/core/java/android/service/carrier/ICarrierMessagingService.aidl
+++ b/core/java/android/service/carrier/ICarrierMessagingService.aidl
@@ -48,9 +48,10 @@ oneway interface ICarrierMessagingService {
* @param text the text to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
* @param callback the callback to notify upon completion
*/
- void sendTextSms(String text, int subId, String destAddress,
+ void sendTextSms(String text, int subId, String destAddress, int sendSmsFlag,
in ICarrierMessagingCallback callback);
/**
@@ -62,10 +63,11 @@ oneway interface ICarrierMessagingService {
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param destPort port number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
* @param callback the callback to notify upon completion
*/
void sendDataSms(in byte[] data, int subId, String destAddress, int destPort,
- in ICarrierMessagingCallback callback);
+ int sendSmsFlag, in ICarrierMessagingCallback callback);
/**
* Request sending a new multi-part text SMS from the device.
@@ -75,10 +77,11 @@ oneway interface ICarrierMessagingService {
* @param parts the parts of the multi-part text SMS to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
* @param callback the callback to notify upon completion
*/
void sendMultipartTextSms(in List<String> parts, int subId, String destAddress,
- in ICarrierMessagingCallback callback);
+ int sendSmsFlag, in ICarrierMessagingCallback callback);
/**
* Request sending a new MMS PDU from the device.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f09f4d2..968cd72 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -692,7 +692,6 @@ public class ZenModeConfig implements Parcelable {
.authority(SYSTEM_AUTHORITY)
.appendPath(EVENT_PATH)
.appendQueryParameter("calendar", Long.toString(event.calendar))
- .appendQueryParameter("attendance", Integer.toString(event.attendance))
.appendQueryParameter("reply", Integer.toString(event.reply))
.build();
}
@@ -710,22 +709,16 @@ public class ZenModeConfig implements Parcelable {
if (!isEvent) return null;
final EventInfo rt = new EventInfo();
rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L);
- rt.attendance = tryParseInt(conditionId.getQueryParameter("attendance"), 0);
rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
return rt;
}
public static class EventInfo {
- public static final int ATTENDANCE_REQUIRED_OR_OPTIONAL = 0;
- public static final int ATTENDANCE_REQUIRED = 1;
- public static final int ATTENDANCE_OPTIONAL = 2;
-
- public static final int REPLY_ANY = 0;
- public static final int REPLY_ANY_EXCEPT_NO = 1;
+ public static final int REPLY_ANY_EXCEPT_NO = 0;
+ public static final int REPLY_YES_OR_MAYBE = 1;
public static final int REPLY_YES = 2;
public long calendar; // CalendarContract.Calendars._ID, or 0 for any
- public int attendance;
public int reply;
@Override
@@ -738,14 +731,12 @@ public class ZenModeConfig implements Parcelable {
if (!(o instanceof EventInfo)) return false;
final EventInfo other = (EventInfo) o;
return calendar == other.calendar
- && attendance == other.attendance
&& reply == other.reply;
}
public EventInfo copy() {
final EventInfo rt = new EventInfo();
rt.calendar = calendar;
- rt.attendance = attendance;
rt.reply = reply;
return rt;
}
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index 4bc97c9..997d586 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -43,7 +43,7 @@ public class VoiceInteractionServiceInfo {
private String mSessionService;
private String mRecognitionService;
private String mSettingsActivity;
- private boolean mSupportsAssistGesture;
+ private boolean mSupportsAssist;
public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp)
throws PackageManager.NameNotFoundException {
@@ -95,8 +95,8 @@ public class VoiceInteractionServiceInfo {
com.android.internal.R.styleable.VoiceInteractionService_recognitionService);
mSettingsActivity = array.getString(
com.android.internal.R.styleable.VoiceInteractionService_settingsActivity);
- mSupportsAssistGesture = array.getBoolean(
- com.android.internal.R.styleable.VoiceInteractionService_supportsAssistGesture,
+ mSupportsAssist = array.getBoolean(
+ com.android.internal.R.styleable.VoiceInteractionService_supportsAssist,
false);
array.recycle();
if (mSessionService == null) {
@@ -145,7 +145,7 @@ public class VoiceInteractionServiceInfo {
return mSettingsActivity;
}
- public boolean getSupportsAssistGesture() {
- return mSupportsAssistGesture;
+ public boolean getSupportsAssist() {
+ return mSupportsAssist;
}
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 239b386..fc65f63 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -283,15 +283,14 @@ public class DynamicLayout extends Layout
if (reflowed == null) {
reflowed = new StaticLayout(null);
- b = StaticLayout.Builder.obtain(text, where, where + after, getWidth());
+ b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
}
b.setText(text, where, where + after)
.setPaint(getPaint())
.setWidth(getWidth())
.setTextDir(getTextDirectionHeuristic())
- .setSpacingMult(getSpacingMultiplier())
- .setSpacingAdd(getSpacingAdd())
+ .setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
.setEllipsizedWidth(mEllipsizedWidth)
.setEllipsize(mEllipsizeAt)
.setBreakStrategy(mBreakStrategy);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 67794b1..451abea 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.Nullable;
import android.graphics.Paint;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
@@ -46,18 +47,31 @@ public class StaticLayout extends Layout {
static final String TAG = "StaticLayout";
/**
- * Builder for static layouts. It would be better if this were a public
- * API (as it would offer much greater flexibility for adding new options)
- * but for the time being it's just internal.
- *
- * @hide
+ * Builder for static layouts. The builder is a newer pattern for constructing
+ * StaticLayout objects and should be preferred over the constructors,
+ * particularly to access newer features. To build a static layout, first
+ * call {@link #obtain} with the required arguments (text, paint, and width),
+ * then call setters for optional parameters, and finally {@link #build}
+ * to build the StaticLayout object. Parameters not explicitly set will get
+ * default values.
*/
public final static class Builder {
private Builder() {
mNativePtr = nNewBuilder();
}
- public static Builder obtain(CharSequence source, int start, int end, int width) {
+ /**
+ * Obtain a builder for constructing StaticLayout objects
+ *
+ * @param source The text to be laid out, optionally with spans
+ * @param start The index of the start of the text
+ * @param end The index + 1 of the end of the text
+ * @param paint The base paint used for layout
+ * @param width The width in pixels
+ * @return a builder object used for constructing the StaticLayout
+ */
+ public static Builder obtain(CharSequence source, int start, int end, TextPaint paint,
+ int width) {
Builder b = sPool.acquire();
if (b == null) {
b = new Builder();
@@ -67,6 +81,7 @@ public class StaticLayout extends Layout {
b.mText = source;
b.mStart = start;
b.mEnd = end;
+ b.mPaint = paint;
b.mWidth = width;
b.mAlignment = Alignment.ALIGN_NORMAL;
b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
@@ -98,6 +113,18 @@ public class StaticLayout extends Layout {
return setText(source, 0, source.length());
}
+ /**
+ * Set the text. Only useful when re-using the builder, which is done for
+ * the internal implementation of {@link DynamicLayout} but not as part
+ * of normal {@link StaticLayout} usage.
+ *
+ * @param source The text to be laid out, optionally with spans
+ * @param start The index of the start of the text
+ * @param end The index + 1 of the end of the text
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
public Builder setText(CharSequence source, int start, int end) {
mText = source;
mStart = start;
@@ -105,11 +132,27 @@ public class StaticLayout extends Layout {
return this;
}
+ /**
+ * Set the paint. Internal for reuse cases only.
+ *
+ * @param paint The base paint used for layout
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
public Builder setPaint(TextPaint paint) {
mPaint = paint;
return this;
}
+ /**
+ * Set the width. Internal for reuse cases only.
+ *
+ * @param width The width in pixels
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
public Builder setWidth(int width) {
mWidth = width;
if (mEllipsize == null) {
@@ -118,53 +161,126 @@ public class StaticLayout extends Layout {
return this;
}
+ /**
+ * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
+ *
+ * @param alignment Alignment for the resulting {@link StaticLayout}
+ * @return this builder, useful for chaining
+ */
public Builder setAlignment(Alignment alignment) {
mAlignment = alignment;
return this;
}
+ /**
+ * Set the text direction heuristic. The text direction heuristic is used to
+ * resolve text direction based per-paragraph based on the input text. The default is
+ * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+ *
+ * @param textDir text direction heuristic for resolving BiDi behavior.
+ * @return this builder, useful for chaining
+ */
public Builder setTextDir(TextDirectionHeuristic textDir) {
mTextDir = textDir;
return this;
}
- // TODO: combine the following, as they're almost always set together?
- public Builder setSpacingMult(float spacingMult) {
- mSpacingMult = spacingMult;
- return this;
- }
-
- public Builder setSpacingAdd(float spacingAdd) {
+ /**
+ * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
+ * and 1.0 for {@code spacingMult}.
+ *
+ * @param spacingAdd line spacing add
+ * @param spacingMult line spacing multiplier
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setLineSpacing
+ */
+ public Builder setLineSpacing(float spacingAdd, float spacingMult) {
mSpacingAdd = spacingAdd;
+ mSpacingMult = spacingMult;
return this;
}
+ /**
+ * Set whether to include extra space beyond font ascent and descent (which is
+ * needed to avoid clipping in some languages, such as Arabic and Kannada). The
+ * default is {@code true}.
+ *
+ * @param includePad whether to include padding
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setIncludeFontPadding
+ */
public Builder setIncludePad(boolean includePad) {
mIncludePad = includePad;
return this;
}
- // TODO: combine the following?
+ /**
+ * Set the width as used for ellipsizing purposes, if it differs from the
+ * normal layout width. The default is the {@code width}
+ * passed to {@link #obtain}.
+ *
+ * @param ellipsizedWidth width used for ellipsizing, in pixels
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setEllipsize
+ */
public Builder setEllipsizedWidth(int ellipsizedWidth) {
mEllipsizedWidth = ellipsizedWidth;
return this;
}
- public Builder setEllipsize(TextUtils.TruncateAt ellipsize) {
+ /**
+ * Set ellipsizing on the layout. Causes words that are longer than the view
+ * is wide, or exceeding the number of lines (see #setMaxLines) in the case
+ * of {@link android.text.TextUtils.TruncateAt#END} or
+ * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
+ * of broken. The default is
+ * {@code null}, indicating no ellipsis is to be applied.
+ *
+ * @param ellipsize type of ellipsis behavior
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setEllipsize
+ */
+ public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
mEllipsize = ellipsize;
return this;
}
+ /**
+ * Set maximum number of lines. This is particularly useful in the case of
+ * ellipsizing, where it changes the layout of the last line. The default is
+ * unlimited.
+ *
+ * @param maxLines maximum number of lines in the layout
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setMaxLines
+ */
public Builder setMaxLines(int maxLines) {
mMaxLines = maxLines;
return this;
}
+ /**
+ * Set break strategy, useful for selecting high quality or balanced paragraph
+ * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
+ *
+ * @param breakStrategy break strategy for paragraph layout
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setBreakStrategy
+ */
public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
mBreakStrategy = breakStrategy;
return this;
}
+ /**
+ * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
+ * pixels. For lines past the last element in the array, the last element repeats.
+ *
+ * @param leftIndents array of indent values for left margin, in pixels
+ * @param rightIndents array of indent values for right margin, in pixels
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setIndents
+ */
public Builder setIndents(int[] leftIndents, int[] rightIndents) {
int leftLen = leftIndents == null ? 0 : leftIndents.length;
int rightLen = rightIndents == null ? 0 : rightIndents.length;
@@ -218,6 +334,15 @@ public class StaticLayout extends Layout {
nAddReplacementRun(mNativePtr, start, end, width);
}
+ /**
+ * Build the {@link StaticLayout} after options have been set.
+ *
+ * <p>Note: the builder object must not be reused in any way after calling this
+ * method. Setting parameters after calling this method, or calling it a second
+ * time on the same builder object, will likely lead to unexpected results.
+ *
+ * @return the newly constructed {@link StaticLayout} object
+ */
public StaticLayout build() {
StaticLayout result = new StaticLayout(this);
Builder.recycle(this);
@@ -327,12 +452,10 @@ public class StaticLayout extends Layout {
: new Ellipsizer(source),
paint, outerwidth, align, textDir, spacingmult, spacingadd);
- Builder b = Builder.obtain(source, bufstart, bufend, outerwidth)
- .setPaint(paint)
+ Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
.setAlignment(align)
.setTextDir(textDir)
- .setSpacingMult(spacingmult)
- .setSpacingAdd(spacingadd)
+ .setLineSpacing(spacingadd, spacingmult)
.setIncludePad(includepad)
.setEllipsizedWidth(ellipsizedWidth)
.setEllipsize(ellipsize)
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index c942042..ebc2aac 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -792,6 +792,9 @@ public abstract class Transition implements Cloneable {
* views are ignored and only the ids are used).
*/
boolean isValidTarget(View target) {
+ if (target == null) {
+ return false;
+ }
int targetId = target.getId();
if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) {
return false;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index ca37d49..cbf76bc 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -211,10 +211,10 @@ public class TransitionInflater {
.asSubclass(expectedType);
if (c != null) {
constructor = c.getConstructor(sConstructorSignature);
+ constructor.setAccessible(true);
sConstructors.put(className, constructor);
}
}
- constructor.setAccessible(true);
return constructor.newInstance(mContext, attrs);
}
} catch (InstantiationException e) {
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index cd68fd1..ed7fd86 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -19,12 +19,17 @@ package android.transition;
import com.android.internal.R;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.Animator.AnimatorPauseListener;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+
/**
* This transition tracks changes to the visibility of target views in the
* start and end scenes. Visibility is determined not just by the
@@ -286,6 +291,12 @@ public abstract class Visibility extends Transition {
return null;
}
}
+ final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
+ mForcedEndVisibility != -1;
+ if (isForcedVisibility) {
+ // Make sure that we reverse the effect of onDisappear's setTransitionAlpha(0)
+ endValues.view.setTransitionAlpha(1);
+ }
return onAppear(sceneRoot, endValues.view, startValues, endValues);
}
@@ -418,9 +429,9 @@ public abstract class Visibility extends Transition {
sceneRoot.getOverlay().remove(overlayView);
} else {
final View finalOverlayView = overlayView;
- animator.addListener(new AnimatorListenerAdapter() {
+ addListener(new TransitionListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onTransitionEnd(Transition transition) {
finalSceneRoot.getOverlay().remove(finalOverlayView);
}
});
@@ -438,40 +449,10 @@ public abstract class Visibility extends Transition {
}
Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
if (animator != null) {
- final View finalViewToKeep = viewToKeep;
- animator.addListener(new AnimatorListenerAdapter() {
- boolean mCanceled = false;
-
- @Override
- public void onAnimationPause(Animator animation) {
- if (!mCanceled && !isForcedVisibility) {
- finalViewToKeep.setVisibility(finalVisibility);
- }
- }
-
- @Override
- public void onAnimationResume(Animator animation) {
- if (!mCanceled && !isForcedVisibility) {
- finalViewToKeep.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCanceled) {
- if (isForcedVisibility) {
- finalViewToKeep.setTransitionAlpha(0);
- } else {
- finalViewToKeep.setVisibility(finalVisibility);
- }
- }
- }
- });
+ DisappearListener disappearListener = new DisappearListener(viewToKeep,
+ finalVisibility, isForcedVisibility);
+ animator.addListener(disappearListener);
+ addListener(disappearListener);
} else if (!isForcedVisibility) {
viewToKeep.setVisibility(originalVisibility);
}
@@ -517,4 +498,68 @@ public abstract class Visibility extends Transition {
TransitionValues endValues) {
return null;
}
+
+ private static class DisappearListener
+ extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener {
+ private final boolean mIsForcedVisibility;
+ private final View mView;
+ private final int mFinalVisibility;
+
+ boolean mCanceled = false;
+
+ public DisappearListener(View view, int finalVisibility, boolean isForcedVisibility) {
+ this.mView = view;
+ this.mIsForcedVisibility = isForcedVisibility;
+ this.mFinalVisibility = finalVisibility;
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ if (!mCanceled && !mIsForcedVisibility) {
+ mView.setVisibility(mFinalVisibility);
+ }
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ if (!mCanceled && !mIsForcedVisibility) {
+ mView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hideViewWhenNotCanceled();
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ hideViewWhenNotCanceled();
+ }
+
+ private void hideViewWhenNotCanceled() {
+ if (!mCanceled) {
+ if (mIsForcedVisibility) {
+ mView.setTransitionAlpha(0);
+ } else {
+ mView.setVisibility(mFinalVisibility);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 46dd857..48167c8 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -271,7 +271,7 @@ public class DisplayListCanvas extends Canvas {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
- nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk,
+ nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
@@ -281,11 +281,11 @@ public class DisplayListCanvas extends Canvas {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
- nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk,
+ nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
- private static native void nDrawPatch(long renderer, long bitmap, long chunk,
+ private static native void nDrawPatch(long renderer, Bitmap bitmap, long chunk,
float left, float top, float right, float bottom, long paint);
public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index d58e7c0..bc38e1a 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -41,7 +41,7 @@ public class GhostView extends View {
final ViewGroup parent = (ViewGroup) mView.getParent();
setGhostedVisibility(View.INVISIBLE);
parent.mRecreateDisplayList = true;
- parent.getDisplayList();
+ parent.updateDisplayListIfDirty();
}
@Override
@@ -49,7 +49,7 @@ public class GhostView extends View {
if (canvas instanceof DisplayListCanvas) {
DisplayListCanvas dlCanvas = (DisplayListCanvas) canvas;
mView.mRecreateDisplayList = true;
- RenderNode renderNode = mView.getDisplayList();
+ RenderNode renderNode = mView.updateDisplayListIfDirty();
if (renderNode.isValid()) {
dlCanvas.insertReorderBarrier(); // enable shadow for this rendernode
dlCanvas.drawRenderNode(renderNode);
@@ -84,7 +84,7 @@ public class GhostView extends View {
final ViewGroup parent = (ViewGroup) mView.getParent();
if (parent != null) {
parent.mRecreateDisplayList = true;
- parent.getDisplayList();
+ parent.updateDisplayListIfDirty();
}
}
}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 1503728..37312d0 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -590,6 +590,7 @@ public abstract class LayoutInflater {
}
}
constructor = clazz.getConstructor(mConstructorSignature);
+ constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
@@ -615,7 +616,6 @@ public abstract class LayoutInflater {
Object[] args = mConstructorArgs;
args[1] = attrs;
- constructor.setAccessible(true);
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index b49a59e..dc8cadf 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -544,6 +544,7 @@ public class MenuInflater {
try {
Class<?> clazz = mContext.getClassLoader().loadClass(className);
Constructor<?> constructor = clazz.getConstructor(constructorSignature);
+ constructor.setAccessible(true);
return (T) constructor.newInstance(arguments);
} catch (Exception e) {
Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5017a38..91e6d68 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -269,7 +269,7 @@ public class ThreadedRenderer extends HardwareRenderer {
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
- view.getDisplayList();
+ view.updateDisplayListIfDirty();
view.mRecreateDisplayList = false;
}
@@ -285,7 +285,7 @@ public class ThreadedRenderer extends HardwareRenderer {
callbacks.onHardwarePreDraw(canvas);
canvas.insertReorderBarrier();
- canvas.drawRenderNode(view.getDisplayList());
+ canvas.drawRenderNode(view.updateDisplayListIfDirty());
canvas.insertInorderBarrier();
callbacks.onHardwarePostDraw(canvas);
@@ -460,8 +460,6 @@ public class ThreadedRenderer extends HardwareRenderer {
if (buffer != null) {
long[] map = atlas.getMap();
if (map != null) {
- // TODO Remove after fixing b/15425820
- validateMap(context, map);
nSetAtlas(renderProxy, buffer, map);
}
// If IAssetAtlas is not the same class as the IBinder
@@ -476,32 +474,6 @@ public class ThreadedRenderer extends HardwareRenderer {
Log.w(LOG_TAG, "Could not acquire atlas", e);
}
}
-
- private static void validateMap(Context context, long[] map) {
- Log.d("Atlas", "Validating map...");
- HashSet<Long> preloadedPointers = new HashSet<Long>();
-
- // We only care about drawables that hold bitmaps
- final Resources resources = context.getResources();
- final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
-
- final int count = drawables.size();
- ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>();
- for (int i = 0; i < count; i++) {
- drawables.valueAt(i).addAtlasableBitmaps(tmpList);
- for (int j = 0; j < tmpList.size(); j++) {
- preloadedPointers.add(tmpList.get(j).getSkBitmap());
- }
- tmpList.clear();
- }
-
- for (int i = 0; i < map.length; i += 4) {
- if (!preloadedPointers.contains(map[i])) {
- Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
- map[i] = 0;
- }
- }
- }
}
static native void setupShadersDiskCache(String cacheFile);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 81ad5ad..75dc0a2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28,6 +28,7 @@ import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UiThread;
import android.content.ClipData;
import android.content.Context;
import android.content.ContextWrapper;
@@ -700,6 +701,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*
* @see android.view.ViewGroup
*/
+@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
private static final boolean DBG = false;
@@ -8595,7 +8597,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
if (isNestedScrollingEnabled()
&& (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
- || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) {
+ || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+ || action == R.id.accessibilityActionScrollUp
+ || action == R.id.accessibilityActionScrollLeft
+ || action == R.id.accessibilityActionScrollDown
+ || action == R.id.accessibilityActionScrollRight)) {
if (dispatchNestedPrePerformAccessibilityAction(action, arguments)) {
return true;
}
@@ -11091,25 +11097,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
- * completely transparent and 1 means the view is completely opaque.</p>
+ * Sets the opacity of the view to a value from 0 to 1, where 0 means the view is
+ * completely transparent and 1 means the view is completely opaque.
*
- * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant
- * performance implications, especially for large views. It is best to use the alpha property
- * sparingly and transiently, as in the case of fading animations.</p>
+ * <p class="note"><strong>Note:</strong> setting alpha to a translucent value (0 < alpha < 1)
+ * can have significant performance implications, especially for large views. It is best to use
+ * the alpha property sparingly and transiently, as in the case of fading animations.</p>
*
* <p>For a view with a frequently changing alpha, such as during a fading animation, it is
* strongly recommended for performance reasons to either override
- * {@link #hasOverlappingRendering()} to return false if appropriate, or setting a
- * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view.</p>
+ * {@link #hasOverlappingRendering()} to return <code>false</code> if appropriate, or setting a
+ * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view for the duration
+ * of the animation. On versions {@link android.os.Build.VERSION_CODES#MNC} and below,
+ * the default path for rendering an unlayered View with alpha could add multiple milliseconds
+ * of rendering cost, even for simple or small views. Starting with
+ * {@link android.os.Build.VERSION_CODES#MNC}, {@link #LAYER_TYPE_HARDWARE} is automatically
+ * applied to the view at the rendering level.</p>
*
* <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is
* responsible for applying the opacity itself.</p>
*
- * <p>Note that if the view is backed by a
- * {@link #setLayerType(int, android.graphics.Paint) layer} and is associated with a
- * {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an alpha value less than
- * 1.0 will supersede the alpha of the layer paint.</p>
+ * <p>On versions {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and below, note that if
+ * the view is backed by a {@link #setLayerType(int, android.graphics.Paint) layer} and is
+ * associated with a {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an
+ * alpha value less than 1.0 will supersede the alpha of the layer paint.</p>
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#MNC}, setting a translucent alpha
+ * value will clip a View to its bounds, unless the View returns <code>false</code> from
+ * {@link #hasOverlappingRendering}.</p>
*
* @param alpha The opacity of the view.
*
@@ -14702,11 +14717,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null);
}
- private void updateDisplayListIfDirty() {
+ /**
+ * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
+ * @hide
+ */
+ @NonNull
+ public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if (!canHaveDisplayList()) {
// can't populate RenderNode, don't try
- return;
+ return renderNode;
}
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
@@ -14720,7 +14740,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
- return; // no work needed
+ return renderNode; // no work needed
}
// If we got here, we're recreating it. Mark it as such to ensure that
@@ -14769,19 +14789,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
- }
-
- /**
- * Returns a RenderNode with View draw content recorded, which can be
- * used to draw this view again without executing its draw method.
- *
- * @return A RenderNode ready to replay, or null if caching is not enabled.
- *
- * @hide
- */
- public RenderNode getDisplayList() {
- updateDisplayListIfDirty();
- return mRenderNode;
+ return renderNode;
}
private void resetDisplayList() {
@@ -15543,7 +15551,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (drawingWithRenderNode) {
// Delay getting the display list until animation-driven alpha values are
// set up and possibly passed on to the view
- renderNode = getDisplayList();
+ renderNode = updateDisplayListIfDirty();
if (!renderNode.isValid()) {
// Uncommon, but possible. If a view is removed from the hierarchy during the call
// to getDisplayList(), the display list will be marked invalid and we should not
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d0d4201..f240fd6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -18,6 +18,7 @@ package android.view;
import android.animation.LayoutTransition;
import android.annotation.IdRes;
+import android.annotation.UiThread;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -105,6 +106,7 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
* @attr ref android.R.styleable#ViewGroup_splitMotionEvents
* @attr ref android.R.styleable#ViewGroup_layoutMode
*/
+@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
private static final String TAG = "ViewGroup";
@@ -3524,10 +3526,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
private void recreateChildDisplayList(View child) {
- child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED)
- == PFLAG_INVALIDATED;
+ child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
child.mPrivateFlags &= ~PFLAG_INVALIDATED;
- child.getDisplayList();
+ child.updateDisplayListIfDirty();
child.mRecreateDisplayList = false;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4158340..fda6e63 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2322,10 +2322,8 @@ public final class ViewRootImpl implements ViewParent,
* @hide
*/
void outputDisplayList(View view) {
- RenderNode renderNode = view.getDisplayList();
- if (renderNode != null) {
- renderNode.output();
- }
+ RenderNode renderNode = view.updateDisplayListIfDirty();
+ renderNode.output();
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bf4b7ae..c785149 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3500,6 +3500,30 @@ public class AccessibilityNodeInfo implements Parcelable {
new AccessibilityAction(R.id.accessibilityActionScrollToPosition, null);
/**
+ * Action to scroll the node content up.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_UP =
+ new AccessibilityAction(R.id.accessibilityActionScrollUp, null);
+
+ /**
+ * Action to scroll the node content left.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_LEFT =
+ new AccessibilityAction(R.id.accessibilityActionScrollLeft, null);
+
+ /**
+ * Action to scroll the node content down.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_DOWN =
+ new AccessibilityAction(R.id.accessibilityActionScrollDown, null);
+
+ /**
+ * Action to scroll the node content right.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_RIGHT =
+ new AccessibilityAction(R.id.accessibilityActionScrollRight, null);
+
+ /**
* Action that stylus button presses the node.
*/
public static final AccessibilityAction ACTION_STYLUS_BUTTON_PRESS =
@@ -3531,6 +3555,10 @@ public class AccessibilityNodeInfo implements Parcelable {
sStandardActions.add(ACTION_SET_TEXT);
sStandardActions.add(ACTION_SHOW_ON_SCREEN);
sStandardActions.add(ACTION_SCROLL_TO_POSITION);
+ sStandardActions.add(ACTION_SCROLL_UP);
+ sStandardActions.add(ACTION_SCROLL_LEFT);
+ sStandardActions.add(ACTION_SCROLL_DOWN);
+ sStandardActions.add(ACTION_SCROLL_RIGHT);
sStandardActions.add(ACTION_STYLUS_BUTTON_PRESS);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1df43d0..c57a53a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1506,10 +1506,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (isEnabled()) {
if (canScrollUp()) {
info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityAction.ACTION_SCROLL_UP);
info.setScrollable(true);
}
if (canScrollDown()) {
info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityAction.ACTION_SCROLL_DOWN);
info.setScrollable(true);
}
}
@@ -1537,14 +1539,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return true;
}
switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ case R.id.accessibilityActionScrollDown: {
if (isEnabled() && getLastVisiblePosition() < getCount() - 1) {
final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION);
return true;
}
} return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case R.id.accessibilityActionScrollUp: {
if (isEnabled() && mFirstPosition > 0) {
final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION);
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 30c74c0..d8ce60c 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -425,6 +426,7 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
* @see #getFilterQueryProvider()
* @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
*/
+ @WorkerThread
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (mFilterQueryProvider != null) {
return mFilterQueryProvider.runQuery(constraint);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 169aef9..8d35b83 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -318,6 +318,7 @@ public class Editor {
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
+ resumeBlink();
}
void onDetachedFromWindow() {
@@ -327,9 +328,7 @@ public class Editor {
hideError();
}
- if (mBlink != null) {
- mBlink.removeCallbacks(mBlink);
- }
+ suspendBlink();
if (mInsertionPointCursorController != null) {
mInsertionPointCursorController.onDetached();
@@ -1402,12 +1401,11 @@ public class Editor {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
if (imm.isActive(mTextView)) {
- boolean reported = false;
if (ims.mContentChanged || ims.mSelectionModeChanged) {
// We are in extract mode and the content has changed
// in some way... just report complete new text to the
// input method.
- reported = reportExtractedText();
+ reportExtractedText();
}
}
}
@@ -1924,10 +1922,6 @@ public class Editor {
mSuggestionsPopupWindow.show();
}
- boolean areSuggestionsShown() {
- return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
- }
-
void onScrollChanged() {
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
@@ -4625,8 +4619,6 @@ public class Editor {
}
static class InputMethodState {
- Rect mCursorRectInWindow = new Rect();
- float[] mTmpOffset = new float[2];
ExtractedTextRequest mExtractedTextRequest;
final ExtractedText mExtractedText = new ExtractedText();
int mBatchEditNesting;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 324c2aa..0879c5d 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -40,6 +40,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
+import com.android.internal.R;
+
import java.util.List;
/**
@@ -768,7 +770,8 @@ public class HorizontalScrollView extends FrameLayout {
return true;
}
switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ case R.id.accessibilityActionScrollRight: {
if (!isEnabled()) {
return false;
}
@@ -779,7 +782,8 @@ public class HorizontalScrollView extends FrameLayout {
return true;
}
} return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case R.id.accessibilityActionScrollLeft: {
if (!isEnabled()) {
return false;
}
@@ -807,10 +811,12 @@ public class HorizontalScrollView extends FrameLayout {
if (scrollRange > 0) {
info.setScrollable(true);
if (isEnabled() && mScrollX > 0) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT);
}
if (isEnabled() && mScrollX < scrollRange) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT);
}
}
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 10e4db3..5953a98 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -618,7 +618,15 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// remove based on both its position as well as it's current memory usage, as well
// as whether it was directly requested vs. whether it was preloaded by our caching
// mechanism.
- mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition, visibleWindow));
+ int trimIndex = getFarthestPositionFrom(pruneFromPosition, visibleWindow);
+
+ // Need to check that this is a valid index, to cover the case where you have only
+ // a single view in the cache, but it's larger than the max memory limit
+ if (trimIndex < 0) {
+ break;
+ }
+
+ mIndexRemoteViews.remove(trimIndex);
}
// Update the metadata cache
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 2026169..98d61d3 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -819,7 +819,8 @@ public class ScrollView extends FrameLayout {
return false;
}
switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ case R.id.accessibilityActionScrollDown: {
final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
final int targetScrollY = Math.min(mScrollY + viewportHeight, getScrollRange());
if (targetScrollY != mScrollY) {
@@ -827,7 +828,8 @@ public class ScrollView extends FrameLayout {
return true;
}
} return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case R.id.accessibilityActionScrollUp: {
final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
final int targetScrollY = Math.max(mScrollY - viewportHeight, 0);
if (targetScrollY != mScrollY) {
@@ -853,10 +855,13 @@ public class ScrollView extends FrameLayout {
if (scrollRange > 0) {
info.setScrollable(true);
if (mScrollY > 0) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
}
if (mScrollY < scrollRange) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
}
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 774a864..449173f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6630,12 +6630,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// TODO: code duplication with makeSingleLayout()
if (mHintLayout == null) {
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
- mHint.length(), hintWidth)
- .setPaint(mTextPaint)
+ mHint.length(), mTextPaint, hintWidth)
.setAlignment(alignment)
.setTextDir(mTextDir)
- .setSpacingMult(mSpacingMult)
- .setSpacingAdd(mSpacingAdd)
+ .setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy);
if (mLeftIndents != null || mRightIndents != null) {
@@ -6721,12 +6719,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (result == null) {
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
- 0, mTransformed.length(), wantWidth)
- .setPaint(mTextPaint)
+ 0, mTransformed.length(), mTextPaint, wantWidth)
.setAlignment(alignment)
.setTextDir(mTextDir)
- .setSpacingMult(mSpacingMult)
- .setSpacingAdd(mSpacingAdd)
+ .setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy);
if (mLeftIndents != null || mRightIndents != null) {