diff options
Diffstat (limited to 'core/java')
36 files changed, 919 insertions, 404 deletions
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 739015fd2e65..8ad7810a13e8 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -2807,40 +2807,56 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate public void dispatchCreate() { mStateSaved = false; + mExecutingActions = true; moveToState(Fragment.CREATED, false); + mExecutingActions = false; } public void dispatchActivityCreated() { mStateSaved = false; + mExecutingActions = true; moveToState(Fragment.ACTIVITY_CREATED, false); + mExecutingActions = false; } public void dispatchStart() { mStateSaved = false; + mExecutingActions = true; moveToState(Fragment.STARTED, false); + mExecutingActions = false; } public void dispatchResume() { mStateSaved = false; + mExecutingActions = true; moveToState(Fragment.RESUMED, false); + mExecutingActions = false; } public void dispatchPause() { + mExecutingActions = true; moveToState(Fragment.STARTED, false); + mExecutingActions = false; } public void dispatchStop() { + mExecutingActions = true; moveToState(Fragment.STOPPED, false); + mExecutingActions = false; } public void dispatchDestroyView() { + mExecutingActions = true; moveToState(Fragment.CREATED, false); + mExecutingActions = false; } public void dispatchDestroy() { mDestroyed = true; execPendingActions(); + mExecutingActions = true; moveToState(Fragment.INITIALIZING, false); + mExecutingActions = false; mHost = null; mContainer = null; mParent = null; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 2ace0a211dd6..9cb3dd6e4c0a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6343,7 +6343,6 @@ public class DevicePolicyManager { * The settings that can be updated by a profile or device owner with this method are: * <ul> * <li>{@link Settings.Secure#DEFAULT_INPUT_METHOD}</li> - * <li>{@link Settings.Secure#INSTALL_NON_MARKET_APPS}</li> * <li>{@link Settings.Secure#SKIP_FIRST_USE_HINTS}</li> * </ul> * <p> @@ -6352,6 +6351,15 @@ public class DevicePolicyManager { * <li>{@link Settings.Secure#LOCATION_MODE}</li> * </ul> * + * <strong>Note: Starting from Android O, apps should no longer call this method with the + * setting {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS}, which is + * deprecated. Instead, device owners or profile owners should use the restriction + * {@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}. + * If any app targeting {@link android.os.Build.VERSION_CODES#O} or higher calls this method + * with {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS}, + * an {@link UnsupportedOperationException} is thrown. + * </strong> + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param setting The name of the setting to update. * @param value The value to update the setting to. diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 8d385db263df..678c017febe0 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -419,7 +419,7 @@ public class AssistStructure implements Parcelable { mDisplayId = root.getDisplayId(); mRoot = new ViewNode(); - ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false, 0); + ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) { if (forAutoFill) { // NOTE: flags are currently not supported, hence 0 @@ -1059,6 +1059,10 @@ public class AssistStructure implements Parcelable { * <li>Root node (containing the URL of the HTML page) * <li>Child nodes that represent hyperlinks (contains the hyperlink URL). * </ol> + * + * <strong>WARNING:</strong> a {@link android.service.autofill.AutoFillService} should only + * use this URL for auto-fill purposes when it trusts the app generating it (i.e., the app + * defined by {@link AssistStructure#getActivityComponent()}). */ public String getUrl() { return mUrl; @@ -1187,11 +1191,10 @@ public class AssistStructure implements Parcelable { final ViewNode mNode; final boolean mAsync; - ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async, int flags) { + ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) { mAssist = assist; mNode = node; mAsync = async; - mNode.mSanitized = (flags & AUTO_FILL_FLAG_SANITIZED) != 0; } @Override @@ -1429,16 +1432,15 @@ public class AssistStructure implements Parcelable { ViewNode node = new ViewNode(); setAutoFillId(node, forAutoFill, virtualId); mNode.mChildren[index] = node; - return new ViewNodeBuilder(mAssist, node, false, flags); + return new ViewNodeBuilder(mAssist, node, false); } - private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId, - int flags) { + private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId) { synchronized (mAssist) { ViewNode node = new ViewNode(); setAutoFillId(node, forAutoFill, virtualId); mNode.mChildren[index] = node; - ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true, flags); + ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); mAssist.mPendingAsyncChildren.add(builder); return builder; } @@ -1457,12 +1459,12 @@ public class AssistStructure implements Parcelable { @Override public ViewStructure asyncNewChild(int index) { - return asyncNewChild(index, false, 0, 0); + return asyncNewChild(index, false, 0); } @Override public ViewStructure asyncNewChild(int index, int virtualId, int flags) { - return asyncNewChild(index, true, virtualId, flags); + return asyncNewChild(index, true, virtualId); } @Override diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java index 099878b310c5..d2a623e0b13a 100644 --- a/core/java/android/app/backup/BackupManagerMonitor.java +++ b/core/java/android/app/backup/BackupManagerMonitor.java @@ -54,15 +54,31 @@ public class BackupManagerMonitor { public static final String EXTRA_LOG_EVENT_CATEGORY = "android.app.backup.extra.LOG_EVENT_CATEGORY"; + /** + * string: when we have event of id LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER we send the version + * of the backup. + */ + public static final String EXTRA_LOG_OLD_VERSION = + "android.app.backup.extra.LOG_OLD_VERSION"; + // TODO complete this list with all log messages. And document properly. public static final int LOG_EVENT_ID_FULL_BACKUP_TIMEOUT = 4; + public static final int LOG_EVENT_ID_PACKAGE_NOT_FOUND = 12; + public static final int LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT = 15; public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT = 21; + public static final int LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE = 22; + public static final int LOG_EVENT_ID_PACKAGE_NOT_PRESENT = 26; + public static final int LOG_EVENT_ID_APP_HAS_NO_AGENT = 28; + public static final int LOG_EVENT_ID_CANT_FIND_AGENT = 30; public static final int LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT = 31; + public static final int LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER = 36; public static final int LOG_EVENT_ID_FULL_RESTORE_TIMEOUT = 45; public static final int LOG_EVENT_ID_NO_PACKAGES = 49; + + /** * This method will be called each time something important happens on BackupManager. * diff --git a/core/java/android/app/usage/CacheQuotaHint.aidl b/core/java/android/app/usage/CacheQuotaHint.aidl new file mode 100644 index 000000000000..0470ea730b65 --- /dev/null +++ b/core/java/android/app/usage/CacheQuotaHint.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.usage; + +/** {@hide} */ +parcelable CacheQuotaHint;
\ No newline at end of file diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java new file mode 100644 index 000000000000..4b6f99b43a09 --- /dev/null +++ b/core/java/android/app/usage/CacheQuotaHint.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.usage; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * CacheQuotaRequest represents a triplet of a uid, the volume UUID it is stored upon, and + * its usage stats. When processed, it obtains a cache quota as defined by the system which + * allows apps to understand how much cache to use. + * {@hide} + */ +@SystemApi +public final class CacheQuotaHint implements Parcelable { + public static final long QUOTA_NOT_SET = -1; + private final String mUuid; + private final int mUid; + private final UsageStats mUsageStats; + private final long mQuota; + + /** + * Create a new request. + * @param builder A builder for this object. + */ + public CacheQuotaHint(Builder builder) { + this.mUuid = builder.mUuid; + this.mUid = builder.mUid; + this.mUsageStats = builder.mUsageStats; + this.mQuota = builder.mQuota; + } + + public String getVolumeUuid() { + return mUuid; + } + + public int getUid() { + return mUid; + } + + public long getQuota() { + return mQuota; + } + + public UsageStats getUsageStats() { + return mUsageStats; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mUuid); + dest.writeInt(mUid); + dest.writeLong(mQuota); + dest.writeParcelable(mUsageStats, 0); + } + + @Override + public int describeContents() { + return 0; + } + + public static final class Builder { + private String mUuid; + private int mUid; + private UsageStats mUsageStats; + private long mQuota; + + public Builder() { + } + + public Builder(CacheQuotaHint hint) { + setVolumeUuid(hint.getVolumeUuid()); + setUid(hint.getUid()); + setUsageStats(hint.getUsageStats()); + setQuota(hint.getQuota()); + } + + public @NonNull Builder setVolumeUuid(@Nullable String uuid) { + mUuid = uuid; + return this; + } + + public @NonNull Builder setUid(int uid) { + Preconditions.checkArgumentPositive(uid, "Proposed uid was not positive."); + mUid = uid; + return this; + } + + public @NonNull Builder setUsageStats(@Nullable UsageStats stats) { + mUsageStats = stats; + return this; + } + + public @NonNull Builder setQuota(long quota) { + Preconditions.checkArgument((quota >= QUOTA_NOT_SET)); + mQuota = quota; + return this; + } + + public @NonNull CacheQuotaHint build() { + Preconditions.checkNotNull(mUsageStats); + return new CacheQuotaHint(this); + } + } + + public static final Parcelable.Creator<CacheQuotaHint> CREATOR = + new Creator<CacheQuotaHint>() { + @Override + public CacheQuotaHint createFromParcel(Parcel in) { + final Builder builder = new Builder(); + return builder.setVolumeUuid(in.readString()) + .setUid(in.readInt()) + .setQuota(in.readLong()) + .setUsageStats(in.readParcelable(UsageStats.class.getClassLoader())) + .build(); + } + + @Override + public CacheQuotaHint[] newArray(int size) { + return new CacheQuotaHint[size]; + } + }; +} diff --git a/core/java/android/app/usage/CacheQuotaService.java b/core/java/android/app/usage/CacheQuotaService.java new file mode 100644 index 000000000000..b9430ab97e06 --- /dev/null +++ b/core/java/android/app/usage/CacheQuotaService.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.usage; + +import android.annotation.SystemApi; +import android.app.Service; +import android.app.usage.ICacheQuotaService; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteCallback; +import android.util.Log; +import android.util.Pair; + +import java.util.List; + +/** + * CacheQuoteService defines a service which accepts cache quota requests and processes them, + * thereby filling out how much quota each request deserves. + * {@hide} + */ +@SystemApi +public abstract class CacheQuotaService extends Service { + private static final String TAG = "CacheQuotaService"; + + /** + * The {@link Intent} action that must be declared as handled by a service + * in its manifest for the system to recognize it as a quota providing service. + */ + public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService"; + + /** {@hide} **/ + public static final String REQUEST_LIST_KEY = "requests"; + + private CacheQuotaServiceWrapper mWrapper; + private Handler mHandler; + + @Override + public void onCreate() { + super.onCreate(); + mWrapper = new CacheQuotaServiceWrapper(); + mHandler = new ServiceHandler(getMainLooper()); + } + + @Override + public IBinder onBind(Intent intent) { + return mWrapper; + } + + /** + * Processes the cache quota list upon receiving a list of requests. + * @param requests A list of cache quotas to fulfill. + * @return A completed list of cache quota requests. + */ + public abstract List<CacheQuotaHint> onComputeCacheQuotaHints( + List<CacheQuotaHint> requests); + + private final class CacheQuotaServiceWrapper extends ICacheQuotaService.Stub { + @Override + public void computeCacheQuotaHints( + RemoteCallback callback, List<CacheQuotaHint> requests) { + final Pair<RemoteCallback, List<CacheQuotaHint>> pair = + Pair.create(callback, requests); + Message msg = mHandler.obtainMessage(ServiceHandler.MSG_SEND_LIST, pair); + mHandler.sendMessage(msg); + } + } + + private final class ServiceHandler extends Handler { + public static final int MSG_SEND_LIST = 1; + + public ServiceHandler(Looper looper) { + super(looper, null, true); + } + + @Override + public void handleMessage(Message msg) { + final int action = msg.what; + switch (action) { + case MSG_SEND_LIST: + final Pair<RemoteCallback, List<CacheQuotaHint>> pair = + (Pair<RemoteCallback, List<CacheQuotaHint>>) msg.obj; + List<CacheQuotaHint> processed = onComputeCacheQuotaHints(pair.second); + final Bundle data = new Bundle(); + data.putParcelableList(REQUEST_LIST_KEY, processed); + + final RemoteCallback callback = pair.first; + callback.sendResult(data); + break; + default: + Log.w(TAG, "Handling unknown message: " + action); + } + } + } +} diff --git a/core/java/android/app/usage/ICacheQuotaService.aidl b/core/java/android/app/usage/ICacheQuotaService.aidl new file mode 100644 index 000000000000..8d984e0bf7b4 --- /dev/null +++ b/core/java/android/app/usage/ICacheQuotaService.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.usage; + +import android.os.RemoteCallback; +import android.app.usage.CacheQuotaHint; + +/** {@hide} */ +oneway interface ICacheQuotaService { + void computeCacheQuotaHints(in RemoteCallback callback, in List<CacheQuotaHint> requests); +} diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl index 62ebf6085aba..f4c18dda5160 100644 --- a/core/java/android/app/usage/IStorageStatsManager.aidl +++ b/core/java/android/app/usage/IStorageStatsManager.aidl @@ -21,6 +21,7 @@ import android.app.usage.ExternalStorageStats; /** {@hide} */ interface IStorageStatsManager { + boolean isQuotaSupported(String volumeUuid, String callingPackage); long getTotalBytes(String volumeUuid, String callingPackage); long getFreeBytes(String volumeUuid, String callingPackage); StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage); diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java index 9d30771aaeba..7d4efb9c108b 100644 --- a/core/java/android/app/usage/StorageStatsManager.java +++ b/core/java/android/app/usage/StorageStatsManager.java @@ -46,6 +46,15 @@ public class StorageStatsManager { mService = Preconditions.checkNotNull(service); } + /** {@hide} */ + public boolean isQuotaSupported(String volumeUuid) { + try { + return mService.isQuotaSupported(volumeUuid, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Return the total space on the requested storage volume. * <p> diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index a6f91fe17227..08595dd1b3b4 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -19,7 +19,7 @@ package android.app.usage; import android.content.ComponentName; import android.content.res.Configuration; -import java.io.IOException; +import java.util.List; /** * UsageStatsManager local system service interface. @@ -127,4 +127,7 @@ public abstract class UsageStatsManagerInternal { public abstract void applyRestoredPayload(int user, String key, byte[] payload); + /* Cache Quota Service API */ + public abstract List<UsageStats> queryUsageStatsForUser( + int userId, int interval, long beginTime, long endTime); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b0505ac4a3a2..28068c54744c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2873,6 +2873,7 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @SystemApi public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED"; @@ -3313,6 +3314,16 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR"; /** + * Broadcast intent sent by the RecoverySystem to inform listeners that a master clear (wipe) + * is about to be performed. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_MASTER_CLEAR_NOTIFICATION + = "android.intent.action.MASTER_CLEAR_NOTIFICATION"; + + /** * Boolean intent extra to be used with {@link #ACTION_MASTER_CLEAR} in order to force a factory * reset even if {@link android.os.UserManager#DISALLOW_FACTORY_RESET} is set. * @@ -3384,6 +3395,17 @@ public class Intent implements Parcelable, Cloneable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT"; + + /** + * Broadcast Action: The sim card state has changed. + * For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here + * because TelephonyIntents is an internal class. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; + /** * The name of the extra used to define the text to be processed, as a * CharSequence. Note that this may be a styled CharSequence, so you must use diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index d1000ade1ed0..13b9206b12ee 100644 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -988,49 +988,31 @@ public class KeyboardView extends View implements View.OnClickListener { if (mAccessibilityManager.isEnabled()) { AccessibilityEvent event = AccessibilityEvent.obtain(eventType); onInitializeAccessibilityEvent(event); - String text = null; - // This is very efficient since the properties are cached. - final boolean speakPassword = Settings.Secure.getIntForUser( - mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, - UserHandle.USER_CURRENT_OR_SELF) != 0; - // Add text only if password announcement is enabled or if headset is - // used to avoid leaking passwords. - if (speakPassword || mAudioManager.isBluetoothA2dpOn() - || mAudioManager.isWiredHeadsetOn()) { - switch (code) { - case Keyboard.KEYCODE_ALT: - text = mContext.getString(R.string.keyboardview_keycode_alt); - break; - case Keyboard.KEYCODE_CANCEL: - text = mContext.getString(R.string.keyboardview_keycode_cancel); - break; - case Keyboard.KEYCODE_DELETE: - text = mContext.getString(R.string.keyboardview_keycode_delete); - break; - case Keyboard.KEYCODE_DONE: - text = mContext.getString(R.string.keyboardview_keycode_done); - break; - case Keyboard.KEYCODE_MODE_CHANGE: - text = mContext.getString(R.string.keyboardview_keycode_mode_change); - break; - case Keyboard.KEYCODE_SHIFT: - text = mContext.getString(R.string.keyboardview_keycode_shift); - break; - case '\n': - text = mContext.getString(R.string.keyboardview_keycode_enter); - break; - default: - text = String.valueOf((char) code); - } - } else if (!mHeadsetRequiredToHearPasswordsAnnounced) { - // We want the waring for required head set to be send with both the - // hover enter and hover exit event, so set the flag after the exit. - if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { - mHeadsetRequiredToHearPasswordsAnnounced = true; - } - text = mContext.getString(R.string.keyboard_headset_required_to_hear_password); - } else { - text = mContext.getString(R.string.keyboard_password_character_no_headset); + final String text; + switch (code) { + case Keyboard.KEYCODE_ALT: + text = mContext.getString(R.string.keyboardview_keycode_alt); + break; + case Keyboard.KEYCODE_CANCEL: + text = mContext.getString(R.string.keyboardview_keycode_cancel); + break; + case Keyboard.KEYCODE_DELETE: + text = mContext.getString(R.string.keyboardview_keycode_delete); + break; + case Keyboard.KEYCODE_DONE: + text = mContext.getString(R.string.keyboardview_keycode_done); + break; + case Keyboard.KEYCODE_MODE_CHANGE: + text = mContext.getString(R.string.keyboardview_keycode_mode_change); + break; + case Keyboard.KEYCODE_SHIFT: + text = mContext.getString(R.string.keyboardview_keycode_shift); + break; + case '\n': + text = mContext.getString(R.string.keyboardview_keycode_enter); + break; + default: + text = String.valueOf((char) code); } event.getText().add(text); mAccessibilityManager.sendAccessibilityEvent(event); diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 719a9577f80e..e6f3021613c4 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1442,8 +1442,8 @@ public class ConnectivityManager { l.networkCapabilities = netCap; l.delay = delay; l.expireSequenceNumber = 0; - l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0, - REQUEST, type); + l.networkRequest = sendRequestForNetwork( + netCap, l.networkCallback, 0, REQUEST, type, getDefaultHandler()); if (l.networkRequest == null) return null; sLegacyRequests.put(netCap, l); sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay); @@ -1453,7 +1453,7 @@ public class ConnectivityManager { private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) { if (delay >= 0) { Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay); - CallbackHandler handler = getHandler(); + CallbackHandler handler = getDefaultHandler(); Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); handler.sendMessageDelayed(msg, delay); } @@ -2780,6 +2780,10 @@ public class ConnectivityManager { super(looper); } + CallbackHandler(Handler handler) { + this(handler.getLooper()); + } + @Override public void handleMessage(Message message) { NetworkRequest request = getObject(message, NetworkRequest.class); @@ -2889,7 +2893,7 @@ public class ConnectivityManager { } } - private CallbackHandler getHandler() { + private CallbackHandler getDefaultHandler() { synchronized (sCallbacks) { if (sCallbackHandler == null) { sCallbackHandler = new CallbackHandler(ConnectivityThread.getInstanceLooper()); @@ -2904,11 +2908,6 @@ public class ConnectivityManager { private static final int LISTEN = 1; private static final int REQUEST = 2; - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallback callback, int timeoutMs, int action, int legacyType) { - return sendRequestForNetwork(need, callback, timeoutMs, action, legacyType, getHandler()); - } - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, int timeoutMs, int action, int legacyType, CallbackHandler handler) { if (callback == null) { @@ -2954,8 +2953,18 @@ public class ConnectivityManager { */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs, int legacyType) { - sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST, - legacyType); + requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler()); + } + + /** + * Helper function to request a network with a particular legacy type. + * @hide + */ + private void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, + int timeoutMs, int legacyType, Handler handler) { + CallbackHandler cbHandler = new CallbackHandler(handler); + NetworkCapabilities nc = request.networkCapabilities; + sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler); } /** @@ -2981,15 +2990,51 @@ public class ConnectivityManager { * {@link android.provider.Settings.System#canWrite}.</p> * * @param request {@link NetworkRequest} describing this request. - * @param networkCallback The {@link NetworkCallback} to be utilized for this - * request. Note the callback must not be shared - they - * uniquely specify this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * The callback is invoked on the default internal Handler. * @throws IllegalArgumentException if {@code request} specifies any mutable * {@code NetworkCapabilities}. */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) { - requestNetwork(request, networkCallback, 0, - inferLegacyTypeForNetworkCapabilities(request.networkCapabilities)); + requestNetwork(request, networkCallback, getDefaultHandler()); + } + + /** + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. + * + * This {@link NetworkRequest} will live until released via + * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. + * Status of the request can be followed by listening to the various + * callbacks described in {@link NetworkCallback}. The {@link Network} + * can be used to direct traffic to the network. + * <p>It is presently unsupported to request a network with mutable + * {@link NetworkCapabilities} such as + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL} + * as these {@code NetworkCapabilities} represent states that a particular + * network may never attain, and whether a network will attain these states + * is unknown prior to bringing up the network so the framework does not + * know how to go about satisfing a request with these capabilities. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @throws IllegalArgumentException if {@code request} specifies any mutable + * {@code NetworkCapabilities}. + * @hide + */ + public void requestNetwork( + NetworkRequest request, NetworkCallback networkCallback, Handler handler) { + int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); + CallbackHandler cbHandler = new CallbackHandler(handler); + requestNetwork(request, networkCallback, 0, legacyType, cbHandler); } /** @@ -3007,17 +3052,48 @@ public class ConnectivityManager { * {@link android.provider.Settings.System#canWrite}.</p> * * @param request {@link NetworkRequest} describing this request. - * @param networkCallback The callbacks to be utilized for this request. Note - * the callbacks must not be shared - they uniquely specify - * this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * The callback is invoked on the default internal Handler. * @param timeoutMs The time in milliseconds to attempt looking for a suitable network * before {@link NetworkCallback#onUnavailable()} is called. * @hide */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs) { - requestNetwork(request, networkCallback, timeoutMs, - inferLegacyTypeForNetworkCapabilities(request.networkCapabilities)); + int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); + requestNetwork(request, networkCallback, timeoutMs, legacyType); + } + + + /** + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited + * by a timeout. + * + * This function behaves identically to the non-timedout version, but if a suitable + * network is not found within the given time (in milliseconds) the + * {@link NetworkCallback#onUnavailable} callback is called. The request must + * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @param timeoutMs The time in milliseconds to attempt looking for a suitable network + * before {@link NetworkCallback#onUnavailable} is called. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * + * @hide + */ + public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, + int timeoutMs, Handler handler) { + int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); + CallbackHandler cbHandler = new CallbackHandler(handler); + requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler); } /** @@ -3131,9 +3207,30 @@ public class ConnectivityManager { * @param request {@link NetworkRequest} describing this request. * @param networkCallback The {@link NetworkCallback} that the system will call as suitable * networks change state. + * The callback is invoked on the default internal Handler. */ public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) { - sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE); + registerNetworkCallback(request, networkCallback, getDefaultHandler()); + } + + /** + * Registers to receive notifications about all networks which satisfy the given + * {@link NetworkRequest}. The callbacks will continue to be called until + * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. + * + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} that the system will call as suitable + * networks change state. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @hide + */ + public void registerNetworkCallback( + NetworkRequest request, NetworkCallback networkCallback, Handler handler) { + CallbackHandler cbHandler = new CallbackHandler(handler); + NetworkCapabilities nc = request.networkCapabilities; + sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler); } /** @@ -3185,8 +3282,25 @@ public class ConnectivityManager { * * @param networkCallback The {@link NetworkCallback} that the system will call as the * system default network changes. + * The callback is invoked on the default internal Handler. */ public void registerDefaultNetworkCallback(NetworkCallback networkCallback) { + registerDefaultNetworkCallback(networkCallback, getDefaultHandler()); + } + + /** + * Registers to receive notifications about changes in the system default network. The callbacks + * will continue to be called until either the application exits or + * {@link #unregisterNetworkCallback(NetworkCallback)} is called. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. + * + * @param networkCallback The {@link NetworkCallback} that the system will call as the + * system default network changes. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @hide + */ + public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) { // This works because if the NetworkCapabilities are null, // ConnectivityService takes them from the default request. // @@ -3194,7 +3308,8 @@ public class ConnectivityManager { // capabilities, this request is guaranteed, at all times, to be // satisfied by the same network, if any, that satisfies the default // request, i.e., the system default network. - sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE); + CallbackHandler cbHandler = new CallbackHandler(handler); + sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler); } /** diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 7c015deae8f5..280fa2ca9496 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -432,14 +432,13 @@ public class FileUtils { */ public static boolean contains(File dir, File file) { if (dir == null || file == null) return false; + return contains(dir.getAbsolutePath(), file.getAbsolutePath()); + } - String dirPath = dir.getAbsolutePath(); - String filePath = file.getAbsolutePath(); - + public static boolean contains(String dirPath, String filePath) { if (dirPath.equals(filePath)) { return true; } - if (!dirPath.endsWith("/")) { dirPath += "/"; } diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 7702c174ba2f..8882672c5210 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -546,6 +546,25 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } /** + * Return the filesystem path of the real file on disk that is represented + * by the given {@link FileDescriptor}. + * + * @hide + */ + public static File getFile(FileDescriptor fd) throws IOException { + try { + final String path = Os.readlink("/proc/self/fd/" + fd.getInt$()); + if (OsConstants.S_ISREG(Os.stat(path).st_mode)) { + return new File(path); + } else { + throw new IOException("Not a regular file: " + path); + } + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + } + + /** * Retrieve the actual FileDescriptor associated with this object. * * @return Returns the FileDescriptor associated with this object. diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 9e35bf68d78c..2cc4b7170f38 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -293,6 +293,6 @@ interface IStorageManager { ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74; long getCacheQuotaBytes(String volumeUuid, int uid) = 75; long getCacheSizeBytes(String volumeUuid, int uid) = 76; - long getAllocatableBytes(String path, int flags) = 77; - void allocateBytes(String path, long bytes, int flags) = 78; + long getAllocatableBytes(String volumeUuid, int flags) = 77; + void allocateBytes(String volumeUuid, long bytes, int flags) = 78; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 2a3c03d1f84f..e070c6e8423f 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,6 +16,7 @@ package android.os.storage; +import static android.net.TrafficStats.GB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import android.annotation.IntDef; @@ -675,6 +676,36 @@ public class StorageManager { } /** {@hide} */ + public @Nullable String findUuidForPath(File path) { + Preconditions.checkNotNull(path); + final String pathString = path.getAbsolutePath(); + if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { + return StorageManager.UUID_PRIVATE_INTERNAL; + } + try { + for (VolumeInfo vol : mStorageManager.getVolumes(0)) { + if (vol.path != null && FileUtils.contains(vol.path, pathString)) { + // TODO: verify that emulated adopted devices have UUID of + // underlying volume + return vol.fsUuid; + } + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + throw new IllegalStateException("Failed to find a storage device for " + path); + } + + /** {@hide} */ + public @Nullable File findPathForUuid(String volumeUuid) { + final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid); + if (vol != null) { + return vol.getPath(); + } + throw new IllegalStateException("Failed to find a storage device for " + volumeUuid); + } + + /** {@hide} */ public @NonNull List<VolumeInfo> getVolumes() { try { return Arrays.asList(mStorageManager.getVolumes(0)); @@ -1069,9 +1100,12 @@ public class StorageManager { throw new IllegalStateException("Missing primary storage"); } - /** {@hide} */ - private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; + private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5; private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; + + private static final int DEFAULT_CACHE_PERCENTAGE = 10; + private static final long DEFAULT_CACHE_MAX_BYTES = 5 * GB_IN_BYTES; + private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; /** @@ -1102,6 +1136,23 @@ public class StorageManager { } /** + * Return the minimum number of bytes of storage on the device that should + * be reserved for cached data. + * + * @hide + */ + public long getStorageCacheBytes(File path) { + final long cachePercent = Settings.Global.getInt(mResolver, + Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE); + final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100; + + final long maxCacheBytes = Settings.Global.getLong(mResolver, + Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES); + + return Math.min(cacheBytes, maxCacheBytes); + } + + /** * Return the number of available bytes at which the given path is * considered full. * @@ -1409,40 +1460,37 @@ public class StorageManager { } /** - * Return quota size in bytes for cached data belonging to the calling app. + * Return quota size in bytes for all cached data belonging to the calling + * app on the filesystem that hosts the given path. * <p> * If your app goes above this quota, your cached files will be some of the * first to be deleted when additional disk space is needed. Conversely, if * your app stays under this quota, your cached files will be some of the * last to be deleted when additional disk space is needed. * <p> - * This quota may change over time depending on how frequently the user + * This quota will change over time depending on how frequently the user * interacts with your app, and depending on how much disk space is used. - * <p> - * Cached data tracked by this method always includes - * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and - * it also includes {@link Context#getExternalCacheDir()} if the primary - * shared/external storage is hosted on the same storage device as your - * private data. * <p class="note"> * Note: if your app uses the {@code android:sharedUserId} manifest feature, * then cached data for all packages in your shared UID is tracked together * as a single unit. * </p> * - * @see #getCacheSizeBytes() + * @see #getCacheSizeBytes(File) */ - public long getCacheQuotaBytes() { + public long getCacheQuotaBytes(File path) { try { + final String volumeUuid = findUuidForPath(path); final ApplicationInfo app = mContext.getApplicationInfo(); - return mStorageManager.getCacheQuotaBytes(app.volumeUuid, app.uid); + return mStorageManager.getCacheQuotaBytes(volumeUuid, app.uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Return total size in bytes of cached data belonging to the calling app. + * Return total size in bytes of all cached data belonging to the calling + * app on the filesystem that hosts the given path. * <p> * Cached data tracked by this method always includes * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and @@ -1457,67 +1505,38 @@ public class StorageManager { * * @see #getCacheQuotaBytes() */ - public long getCacheSizeBytes() { + public long getCacheSizeBytes(File path) { try { + final String volumeUuid = findUuidForPath(path); final ApplicationInfo app = mContext.getApplicationInfo(); - return mStorageManager.getCacheSizeBytes(app.volumeUuid, app.uid); + return mStorageManager.getCacheSizeBytes(volumeUuid, app.uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - /** - * Return quota size in bytes for cached data on primary shared/external - * storage belonging to the calling app. - * <p> - * If primary shared/external storage is hosted on the same storage device - * as your private data, this method will return -1, since all data stored - * under {@link Context#getExternalCacheDir()} will be counted under - * {@link #getCacheQuotaBytes()}. - * <p class="note"> - * Note: if your app uses the {@code android:sharedUserId} manifest feature, - * then cached data for all packages in your shared UID is tracked together - * as a single unit. - * </p> - */ + /** @removed */ + @Deprecated + public long getCacheQuotaBytes() { + return getCacheQuotaBytes(mContext.getCacheDir()); + } + + /** @removed */ + @Deprecated + public long getCacheSizeBytes() { + return getCacheSizeBytes(mContext.getCacheDir()); + } + + /** @removed */ + @Deprecated public long getExternalCacheQuotaBytes() { - final ApplicationInfo app = mContext.getApplicationInfo(); - final String primaryUuid = getPrimaryStorageUuid(); - if (Objects.equals(app.volumeUuid, primaryUuid)) { - return -1; - } - try { - return mStorageManager.getCacheQuotaBytes(primaryUuid, app.uid); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCacheQuotaBytes(mContext.getExternalCacheDir()); } - /** - * Return total size in bytes of cached data on primary shared/external - * storage belonging to the calling app. - * <p> - * If primary shared/external storage is hosted on the same storage device - * as your private data, this method will return -1, since all data stored - * under {@link Context#getExternalCacheDir()} will be counted under - * {@link #getCacheQuotaBytes()}. - * <p class="note"> - * Note: if your app uses the {@code android:sharedUserId} manifest feature, - * then cached data for all packages in your shared UID is tracked together - * as a single unit. - * </p> - */ + /** @removed */ + @Deprecated public long getExternalCacheSizeBytes() { - final ApplicationInfo app = mContext.getApplicationInfo(); - final String primaryUuid = getPrimaryStorageUuid(); - if (Objects.equals(app.volumeUuid, primaryUuid)) { - return -1; - } - try { - return mStorageManager.getCacheSizeBytes(primaryUuid, app.uid); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCacheSizeBytes(mContext.getExternalCacheDir()); } /** @@ -1551,28 +1570,30 @@ public class StorageManager { * Return the maximum number of new bytes that your app can allocate for * itself using {@link #allocateBytes(File, long, int)} at the given path. * This value is typically larger than {@link File#getUsableSpace()}, since - * the system may automatically delete cached files to satisfy your request. + * the system may be willing to delete cached files to satisfy an allocation + * request. * <p> * This method is best used as a pre-flight check, such as deciding if there * is enough space to store an entire music album before you allocate space * for each audio file in the album. Attempts to allocate disk space beyond - * this value will fail. + * the returned value will fail. * <p class="note"> * Note: if your app uses the {@code android:sharedUserId} manifest feature, * then allocatable space for all packages in your shared UID is tracked * together as a single unit. * </p> * - * @param file the directory where you're considering allocating disk space, + * @param path the path where you're considering allocating disk space, * since allocatable space can vary widely depending on the * underlying storage device. * @param flags to apply to the request. * @return the maximum number of new bytes that the calling app can allocate * using {@link #allocateBytes(File, long, int)}. */ - public long getAllocatableBytes(File file, @AllocateFlags int flags) throws IOException { + public long getAllocatableBytes(File path, @AllocateFlags int flags) throws IOException { try { - return mStorageManager.getAllocatableBytes(file.getAbsolutePath(), flags); + final String volumeUuid = findUuidForPath(path); + return mStorageManager.getAllocatableBytes(volumeUuid, flags); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); throw new RuntimeException(e); @@ -1594,14 +1615,15 @@ public class StorageManager { * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee * that bytes are allocated to an opened file. * - * @param file the directory where you'd like to allocate disk space. + * @param path the path where you'd like to allocate disk space. * @param bytes the number of bytes to allocate. * @param flags to apply to the request. * @see #getAllocatableBytes(File, int) */ - public void allocateBytes(File file, long bytes, @AllocateFlags int flags) throws IOException { + public void allocateBytes(File path, long bytes, @AllocateFlags int flags) throws IOException { try { - mStorageManager.allocateBytes(file.getAbsolutePath(), bytes, flags); + final String volumeUuid = findUuidForPath(path); + mStorageManager.allocateBytes(volumeUuid, bytes, flags); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); } catch (RemoteException e) { @@ -1610,37 +1632,39 @@ public class StorageManager { } /** - * Allocate the requested number of bytes for your application to use at the - * given path. This will cause the system to delete any cached files + * Allocate the requested number of bytes for your application to use in the + * given open file. This will cause the system to delete any cached files * necessary to satisfy your request. * <p> * Attempts to allocate disk space beyond the value returned by * {@link #getAllocatableBytes(File, int)} will fail. * <p> - * This method guarantees that bytes are allocated to the opened file, - * otherwise it will throw if fast allocation not possible. Fast allocation - * is typically only supported in private app data directories, and on - * shared/external storage devices which are emulated. + * This method guarantees that bytes have been allocated to the opened file, + * otherwise it will throw if fast allocation is not possible. Fast + * allocation is typically only supported in private app data directories, + * and on shared/external storage devices which are emulated. * - * @param fd the directory where you'd like to allocate disk space. - * @param bytes the number of bytes to allocate. + * @param fd the open file that you'd like to allocate disk space for. + * @param bytes the number of bytes to allocate. This is the desired final + * size of the open file. * @param flags to apply to the request. * @see #getAllocatableBytes(File, int) * @see Environment#isExternalStorageEmulated(File) */ public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags) throws IOException { - final File file; - try { - file = new File(Os.readlink("/proc/self/fd/" + fd.getInt$())); - } catch (ErrnoException e) { - throw e.rethrowAsIOException(); - } + final File file = ParcelFileDescriptor.getFile(fd); for (int i = 0; i < 3; i++) { - allocateBytes(file, bytes, flags); - try { + final long haveBytes = Os.fstat(fd).st_blocks * 512; + final long needBytes = bytes - haveBytes; + + if (needBytes > 0) { + allocateBytes(file, needBytes, flags); + } + Os.posix_fallocate(fd, 0, bytes); + return; } catch (ErrnoException e) { if (e.errno == OsConstants.ENOSPC) { Log.w(TAG, "Odd, not enough space; let's try again?"); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c9b1c9cf2703..6c319abb641b 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5217,6 +5217,18 @@ public final class Settings { public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps"; /** + * A flag to tell {@link com.android.server.devicepolicy.DevicePolicyManagerService} that + * the default for {@link #INSTALL_NON_MARKET_APPS} is reversed for this user on OTA. So it + * can set the restriction {@link android.os.UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES} + * on behalf of the profile owner if needed to make the change transparent for profile + * owners. + * + * @hide + */ + public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED = + "unknown_sources_default_reversed"; + + /** * Comma-separated list of location providers that activities may access. Do not rely on * this value being present in settings.db or on ContentObserver notifications on the * corresponding Uri. @@ -5492,7 +5504,12 @@ public final class Settings { /** * Whether to speak passwords while in accessibility mode. + * + * @deprecated The speaking of passwords is controlled by individual accessibility services. + * Apps should ignore this setting and provide complete information to accessibility + * at all times, which was the behavior when this value was {@code true}. */ + @Deprecated public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; /** @@ -8578,6 +8595,24 @@ public final class Settings { SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes"; /** + * Minimum percentage of storage on the device that is reserved for + * cached data. + * + * @hide + */ + public static final String + SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage"; + + /** + * Maximum bytes of storage on the device that is reserved for cached + * data. + * + * @hide + */ + public static final String + SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes"; + + /** * The maximum reconnect delay for short network outages or when the * network is suspended due to phone use. * diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 59b494c3f8a6..d76d444e7d33 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -106,15 +106,13 @@ public final class Dataset implements Parcelable { private boolean mDestroyed; /** - * Sets the presentation used to visualize this dataset. + * Creates a new builder. * - * @param presentation The presentation view. - * - * @return This builder. + * @param presentation The presentation used to visualize this dataset. */ - public @NonNull Builder setPresentation(@Nullable RemoteViews presentation) { + public Builder(@NonNull RemoteViews presentation) { + Preconditions.checkNotNull(presentation, "presentation must be non-null"); mPresentation = presentation; - return this; } /** @@ -201,9 +199,6 @@ public final class Dataset implements Parcelable { throw new IllegalArgumentException( "at least one value must be set"); } - if (mPresentation == null) { - throw new IllegalArgumentException("presentation must be set"); - } return new Dataset(this); } @@ -225,9 +220,9 @@ public final class Dataset implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mPresentation, flags); parcel.writeTypedArrayList(mFieldIds, flags); parcel.writeTypedArrayList(mFieldValues, flags); - parcel.writeParcelable(mPresentation, flags); parcel.writeParcelable(mAuthentication, flags); } @@ -237,7 +232,7 @@ public final class Dataset implements Parcelable { // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. - final Builder builder = new Builder(); + final Builder builder = new Builder(parcel.readParcelable(null)); final ArrayList<AutoFillId> ids = parcel.readTypedArrayList(null); final ArrayList<AutoFillValue> values = parcel.readTypedArrayList(null); final int idCount = (ids != null) ? ids.size() : 0; @@ -247,7 +242,6 @@ public final class Dataset implements Parcelable { AutoFillValue value = (valueCount > i) ? values.get(i) : null; builder.setValue(id, value); } - builder.setPresentation(parcel.readParcelable(null)); builder.setAuthentication(parcel.readParcelable(null)); return builder.build(); } diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index bac43915281a..91c668ef32e2 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -28,6 +28,8 @@ import android.view.autofill.AutoFillId; import android.view.autofill.AutoFillManager; import android.widget.RemoteViews; +import java.util.ArrayList; + /** * Response for a {@link * AutoFillService#onFillRequest(android.app.assist.AssistStructure, @@ -43,8 +45,7 @@ import android.widget.RemoteViews; * * <pre class="prettyprint"> * new FillResponse.Builder() - * .add(new Dataset.Builder() - * .setPresentation(createPresentation()) + * .add(new Dataset.Builder(createPresentation()) * .setTextFieldValue(id1, "homer") * .setTextFieldValue(id2, "D'OH!") * .build()) @@ -55,13 +56,11 @@ import android.widget.RemoteViews; * * <pre class="prettyprint"> * new FillResponse.Builder() - * .add(new Dataset.Builder() - * .setPresentation(createFirstPresentation()) + * .add(new Dataset.Builder(createFirstPresentation()) * .setTextFieldValue(id1, "homer") * .setTextFieldValue(id2, "D'OH!") * .build()) - * .add(new Dataset.Builder() - * .setPresentation(createSecondPresentation()) + * .add(new Dataset.Builder(createSecondPresentation()) * .setTextFieldValue(id1, "elbarto") * .setTextFieldValue(id2, "cowabonga") * .build()) @@ -85,8 +84,7 @@ import android.widget.RemoteViews; * * <pre class="prettyprint"> * new FillResponse.Builder() - * .add(new Dataset.Builder(") - * .setPresentation(createPresentation()) + * .add(new Dataset.Builder(createPresentation()) * .setTextFieldValue(id1, "Homer") // first name * .setTextFieldValue(id2, "Simpson") // last name * .setTextFieldValue(id3, "742 Evergreen Terrace") // street @@ -114,13 +112,11 @@ import android.widget.RemoteViews; * * <pre class="prettyprint"> * new FillResponse.Builder() - * .add(new Dataset.Builder() - * .setPresentation(createFirstPresentation()) + * .add(new Dataset.Builder(createFirstPresentation()) * .setTextFieldValue(id1, "Homer") * .setTextFieldValue(id2, "Simpson") * .build()) - * .add(new Dataset.Builder() - * .setPresentation(createSecondPresentation()) + * .add(new Dataset.Builder(createSecondPresentation()) * .setTextFieldValue(id1, "Bart") * .setTextFieldValue(id2, "Simpson") * .build()) @@ -132,13 +128,11 @@ import android.widget.RemoteViews; * * <pre class="prettyprint"> * new FillResponse.Builder() - * .add(new Dataset.Builder() - * .setPresentation(createThirdPresentation()) + * .add(new Dataset.Builder(createThirdPresentation()) * .setTextFieldValue(id3, "742 Evergreen Terrace") * .setTextFieldValue(id4, "Springfield") * .build()) - * .add(new Dataset.Builder() - * .setPresentation(createFourthPresentation()) + * .add(new Dataset.Builder(createFourthPresentation()) * .setTextFieldValue(id3, "Springfield Power Plant") * .setTextFieldValue(id4, "Springfield") * .build()) @@ -163,7 +157,7 @@ import android.widget.RemoteViews; */ public final class FillResponse implements Parcelable { - private final ArraySet<Dataset> mDatasets; + private final ArrayList<Dataset> mDatasets; private final ArraySet<AutoFillId> mSavableIds; private final Bundle mExtras; private final RemoteViews mPresentation; @@ -183,7 +177,7 @@ public final class FillResponse implements Parcelable { } /** @hide */ - public @Nullable ArraySet<Dataset> getDatasets() { + public @Nullable ArrayList<Dataset> getDatasets() { return mDatasets; } @@ -207,7 +201,7 @@ public final class FillResponse implements Parcelable { * one dataset or set an authentication intent with a presentation view. */ public static final class Builder { - private ArraySet<Dataset> mDatasets; + private ArrayList<Dataset> mDatasets; private ArraySet<AutoFillId> mSavableIds; private Bundle mExtras; private RemoteViews mPresentation; @@ -215,23 +209,6 @@ public final class FillResponse implements Parcelable { private boolean mDestroyed; /** - * Sets the presentation used to visualize this response. You should - * set this only if you need an authentication as this is the only - * case the response needs to be presented to the user. - * - * @param presentation The presentation view. - * - * @return This builder. - * - * @see #setAuthentication(IntentSender) - */ - public @NonNull - FillResponse.Builder setPresentation(@Nullable RemoteViews presentation) { - mPresentation = presentation; - return this; - } - - /** * Requires a fill response authentication before auto-filling the activity with * any data set in this response. * @@ -257,19 +234,29 @@ public final class FillResponse implements Parcelable { * available data sets some of which may need to be further authenticated, for * example a credit card whose CVV needs to be entered.</p> * + * <p>If you provide an authentication intent you must also provide a presentation + * which is used to visualize visualize the response for triggering the authentication + * flow.</p> + * * <p></><strong>Note:</strong> Do not make the provided pending intent * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the * platform needs to fill in the authentication arguments.</p> * * @param authentication Intent to an activity with your authentication flow. + * @param presentation The presentation to visualize the response. * @return This builder. * * @see android.app.PendingIntent#getIntentSender() - * @see #setPresentation(RemoteViews) */ - public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) { + public @NonNull Builder setAuthentication(@Nullable IntentSender authentication, + @Nullable RemoteViews presentation) { throwIfDestroyed(); + if (authentication == null ^ presentation == null) { + throw new IllegalArgumentException("authentication and presentation" + + " must be both non-null or null"); + } mAuthentication = authentication; + mPresentation = presentation; return this; } @@ -284,7 +271,7 @@ public final class FillResponse implements Parcelable { return this; } if (mDatasets == null) { - mDatasets = new ArraySet<>(); + mDatasets = new ArrayList<>(); } if (!mDatasets.add(dataset)) { return this; @@ -353,10 +340,6 @@ public final class FillResponse implements Parcelable { */ public FillResponse build() { throwIfDestroyed(); - if (mAuthentication == null ^ mPresentation == null) { - throw new IllegalArgumentException("authentication and presentation" - + " must be both non-null or null"); - } if (mAuthentication == null && mDatasets == null && mSavableIds == null) { throw new IllegalArgumentException("need to provide at least one" + " data set or savable ids or an authentication with a presentation"); @@ -398,11 +381,11 @@ public final class FillResponse implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeTypedArraySet(mDatasets, flags); + parcel.writeTypedArrayList(mDatasets, flags); parcel.writeTypedArraySet(mSavableIds, flags); parcel.writeParcelable(mExtras, flags); - parcel.writeParcelable(mPresentation, flags); parcel.writeParcelable(mAuthentication, flags); + parcel.writeParcelable(mPresentation, flags); } public static final Parcelable.Creator<FillResponse> CREATOR = @@ -413,10 +396,10 @@ public final class FillResponse implements Parcelable { // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. final Builder builder = new Builder(); - final ArraySet<Dataset> datasets = parcel.readTypedArraySet(null); + final ArrayList<Dataset> datasets = parcel.readTypedArrayList(null); final int datasetCount = (datasets != null) ? datasets.size() : 0; for (int i = 0; i < datasetCount; i++) { - builder.addDataset(datasets.valueAt(i)); + builder.addDataset(datasets.get(i)); } final ArraySet<AutoFillId> fillIds = parcel.readTypedArraySet(null); final int fillIdCount = (fillIds != null) ? fillIds.size() : 0; @@ -424,8 +407,8 @@ public final class FillResponse implements Parcelable { builder.addSavableFields(fillIds.valueAt(i)); } builder.setExtras(parcel.readParcelable(null)); - builder.setPresentation(parcel.readParcelable(null)); - builder.setAuthentication(parcel.readParcelable(null)); + builder.setAuthentication(parcel.readParcelable(null), + parcel.readParcelable(null)); return builder.build(); } diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 60d8a0fc8894..4b02df86c82e 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -53,16 +53,12 @@ import java.lang.reflect.Array; * @param end End index in the source object. */ private final void copySpans(Spanned src, int start, int end) { - final Object[] spans = src.getSpans(start, end, Object.class); + Object[] spans = src.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { - if (spans[i] instanceof NoCopySpan) { - continue; - } - int st = src.getSpanStart(spans[i]); int en = src.getSpanEnd(spans[i]); - final int fl = src.getSpanFlags(spans[i]); + int fl = src.getSpanFlags(spans[i]); if (st < start) st = start; @@ -82,42 +78,33 @@ import java.lang.reflect.Array; * @param end End index in the source object. */ private final void copySpans(SpannableStringInternal src, int start, int end) { - int count = 0; - boolean includesNoCopySpan = false; - final int[] srcData = src.mSpanData; - final Object[] srcSpans = src.mSpans; - final int limit = src.mSpanCount; - - for (int i = 0; i < limit; i++) { - int spanStart = srcData[i * COLUMNS + START]; - int spanEnd = srcData[i * COLUMNS + END]; - if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; - if (srcSpans[i] instanceof NoCopySpan) { - includesNoCopySpan = true; - continue; - } - count++; - } - - if (count == 0) return; - - if (!includesNoCopySpan && start == 0 && end == src.length()) { + if (start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { + int count = 0; + int[] srcData = src.mSpanData; + int limit = src.mSpanCount; + for (int i = 0; i < limit; i++) { + int spanStart = srcData[i * COLUMNS + START]; + int spanEnd = srcData[i * COLUMNS + END]; + if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; + count++; + } + + if (count == 0) return; + + Object[] srcSpans = src.mSpans; mSpanCount = count; mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount); mSpanData = new int[mSpans.length * COLUMNS]; for (int i = 0, j = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; - if (isOutOfCopyRange(start, end, spanStart, spanEnd) - || srcSpans[i] instanceof NoCopySpan) { - continue; - } + if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; if (spanStart < start) spanStart = start; if (spanEnd > end) spanEnd = end; diff --git a/core/java/android/view/IGraphicsStats.aidl b/core/java/android/view/IGraphicsStats.aidl index c235eb261ee6..e6b750b73406 100644 --- a/core/java/android/view/IGraphicsStats.aidl +++ b/core/java/android/view/IGraphicsStats.aidl @@ -17,10 +17,11 @@ package android.view; import android.os.ParcelFileDescriptor; +import android.view.IGraphicsStatsCallback; /** * @hide */ interface IGraphicsStats { - ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token); + ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback callback); } diff --git a/core/java/android/view/IGraphicsStatsCallback.aidl b/core/java/android/view/IGraphicsStatsCallback.aidl new file mode 100644 index 000000000000..f70e1419c317 --- /dev/null +++ b/core/java/android/view/IGraphicsStatsCallback.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * @hide + */ +oneway interface IGraphicsStatsCallback { + void onRotateGraphicsStatsBuffer(); +} diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 4ceb23628fb4..c66bf87476a7 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -25,9 +25,9 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; -import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.Trace; import android.util.Log; @@ -164,6 +164,18 @@ public final class ThreadedRenderer { public static final String OVERDRAW_PROPERTY_SHOW = "show"; /** + * Defines the rendering pipeline to be used by the ThreadedRenderer. + * + * Possible values: + * "opengl", will use the existing OpenGL renderer + * "skiagl", will use Skia's OpenGL renderer + * "skiavk", will use Skia's Vulkan renderer + * + * @hide + */ + public static final String DEBUG_RENDERER_PROPERTY = "debug.hwui.renderer"; + + /** * Turn on to debug non-rectangular clip operations. * * Possible values: @@ -248,10 +260,10 @@ public final class ThreadedRenderer { * * @return A threaded renderer backed by OpenGL. */ - public static ThreadedRenderer create(Context context, boolean translucent) { + public static ThreadedRenderer create(Context context, boolean translucent, String name) { ThreadedRenderer renderer = null; if (isAvailable()) { - renderer = new ThreadedRenderer(context, translucent); + renderer = new ThreadedRenderer(context, translucent, name); } return renderer; } @@ -275,10 +287,6 @@ public final class ThreadedRenderer { nOverrideProperty(name, value); } - public static void dumpProfileData(byte[] data, FileDescriptor fd) { - nDumpProfileData(data, fd); - } - // Keep in sync with DrawFrameTask.h SYNC_* flags // Nothing interesting to report private static final int SYNC_OK = 0; @@ -334,7 +342,7 @@ public final class ThreadedRenderer { private boolean mEnabled; private boolean mRequested = true; - ThreadedRenderer(Context context, boolean translucent) { + ThreadedRenderer(Context context, boolean translucent, String name) { final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0); @@ -348,6 +356,7 @@ public final class ThreadedRenderer { mRootNode = RenderNode.adopt(rootNodePtr); mRootNode.setClipToBounds(false); mNativeProxy = nCreateProxy(translucent, rootNodePtr); + nSetName(mNativeProxy, name); ProcessInitializer.sInstance.init(context, mNativeProxy); @@ -815,15 +824,6 @@ public final class ThreadedRenderer { } /** - * Optional, sets the name of the renderer. Useful for debugging purposes. - * - * @param name The name of this renderer, can be null - */ - void setName(String name) { - nSetName(mNativeProxy, name); - } - - /** * Blocks until all previously queued work has completed. */ void fence() { @@ -884,20 +884,29 @@ public final class ThreadedRenderer { private static class ProcessInitializer { static ProcessInitializer sInstance = new ProcessInitializer(); - private static IBinder sProcToken; private boolean mInitialized = false; + private Context mAppContext; + private IGraphicsStats mGraphicsStatsService; + private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() { + @Override + public void onRotateGraphicsStatsBuffer() throws RemoteException { + rotateBuffer(); + } + }; + private ProcessInitializer() {} synchronized void init(Context context, long renderProxy) { if (mInitialized) return; mInitialized = true; + mAppContext = context.getApplicationContext(); initSched(context, renderProxy); - initGraphicsStats(context, renderProxy); + initGraphicsStats(); } - private static void initSched(Context context, long renderProxy) { + private void initSched(Context context, long renderProxy) { try { int tid = nGetRenderThreadTid(renderProxy); ActivityManager.getService().setRenderThread(tid); @@ -906,17 +915,28 @@ public final class ThreadedRenderer { } } - private static void initGraphicsStats(Context context, long renderProxy) { + private void initGraphicsStats() { try { IBinder binder = ServiceManager.getService("graphicsstats"); if (binder == null) return; - IGraphicsStats graphicsStatsService = IGraphicsStats.Stub - .asInterface(binder); - sProcToken = new Binder(); - final String pkg = context.getApplicationInfo().packageName; - ParcelFileDescriptor pfd = graphicsStatsService. - requestBufferForProcess(pkg, sProcToken); - nSetProcessStatsBuffer(renderProxy, pfd.getFd()); + mGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder); + requestBuffer(); + } catch (Throwable t) { + Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); + } + } + + private void rotateBuffer() { + nRotateProcessStatsBuffer(); + requestBuffer(); + } + + private void requestBuffer() { + try { + final String pkg = mAppContext.getApplicationInfo().packageName; + ParcelFileDescriptor pfd = mGraphicsStatsService + .requestBufferForProcess(pkg, mGraphicsStatsCallback); + nSetProcessStatsBuffer(pfd.getFd()); pfd.close(); } catch (Throwable t) { Log.w(LOG_TAG, "Could not acquire gfx stats buffer", t); @@ -936,7 +956,8 @@ public final class ThreadedRenderer { static native void setupShadersDiskCache(String cacheFile); - private static native void nSetProcessStatsBuffer(long nativeProxy, int fd); + private static native void nRotateProcessStatsBuffer(); + private static native void nSetProcessStatsBuffer(int fd); private static native int nGetRenderThreadTid(long nativeProxy); private static native long nCreateRootRenderNode(); @@ -981,7 +1002,6 @@ public final class ThreadedRenderer { private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, @DumpFlags int dumpFlags); - private static native void nDumpProfileData(byte[] data, FileDescriptor fd); private static native void nAddRenderNode(long nativeProxy, long rootRenderNode, boolean placeFront); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index df0a161327f0..aa85a985155b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6983,19 +6983,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Called when assist structure is being retrieved from a view as part of an auto-fill request. * - * <p>When implementing this method, subclasses must also: - * - * <ol> - * <li>Implement {@link #autoFill(AutoFillValue)}, {@link #getAutoFillType()} - * and {@link #getAutoFillValue()}. - * <li>Call {@link android.view.autofill.AutoFillManager#virtualValueChanged(View, int, - * AutoFillValue)} when its value changed. - * </ol> + * <p>This method already provides most of what's needed for auto-fill, but should be overridden + * when the view contents does not include PII (Personally Identifiable Information) (so it + * can call {@link ViewStructure#setSanitized(boolean) ViewStructure#setSanitized(true)}). * * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. * @param flags optional flags (currently {@code 0}). */ + @CallSuper public void onProvideAutoFillStructure(ViewStructure structure, int flags) { onProvideStructureForAssistOrAutoFill(structure, true); } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 5d01b4162c6f..f16fcc933b70 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -35,7 +35,7 @@ public class ViewConfiguration { * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in * dips */ - private static final int SCROLL_BAR_SIZE = 10; + private static final int SCROLL_BAR_SIZE = 4; /** * Duration of the fade when scrollbars fade away in milliseconds @@ -346,7 +346,8 @@ public class ViewConfiguration { mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f); mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f); - mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f); + mScrollbarSize = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_scrollbarSize); mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f); mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 6cdd483370b6..c81e9385897f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -887,9 +887,9 @@ public final class ViewRootImpl implements ViewParent, final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0 || insets.top != 0 || insets.bottom != 0; final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets; - mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent); + mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent, + attrs.getTitle().toString()); if (mAttachInfo.mThreadedRenderer != null) { - mAttachInfo.mThreadedRenderer.setName(attrs.getTitle().toString()); mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 9ce23e631451..bc2725f56bf6 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -31,16 +31,6 @@ import android.view.autofill.AutoFillValue; public abstract class ViewStructure { /** - * Flag used when adding virtual views for auto-fill, it indicates the contents of the view - * (such as * {@link android.app.assist.AssistStructure.ViewNode#getText()} and - * {@link android.app.assist.AssistStructure.ViewNode#getAutoFillValue()}) - * can be passed to the {@link - * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure, - * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} call. - */ - public static final int AUTO_FILL_FLAG_SANITIZED = 0x1; - - /** * Set the identifier for this view. * * @param id The view's identifier, as per {@link View#getId View.getId()}. @@ -278,7 +268,7 @@ public abstract class ViewStructure { * * @param index child index * @param virtualId id identifying the virtual child inside the custom view. - * @param flags currently {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}. + * @param flags currently {@code 0}. */ // TODO(b/33197203, b/33802548): add CTS/unit test public abstract ViewStructure newChild(int index, int virtualId, int flags); @@ -299,7 +289,7 @@ public abstract class ViewStructure { * * @param index child index * @param virtualId id identifying the virtual child inside the custom view. - * @param flags currently {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}. + * @param flags currently {@code 0}. */ // TODO(b/33197203, b/33802548): add CTS/unit test public abstract ViewStructure asyncNewChild(int index, int virtualId, int flags); @@ -317,12 +307,19 @@ public abstract class ViewStructure { public abstract void setAutoFillValue(AutoFillValue value); /** - * @hide + * Marks this node as sanitized so its content are sent on {@link + * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure, + * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)}. + * + * <p>Only nodes that does not have PII (Personally Identifiable Information - sensitive data + * such as email addresses, credit card numbers, passwords, etc...) should be marked + * as sanitized; a good rule of thumb is to mark as sanitized nodes whose value were statically + * set from resources. * - * TODO(b/33197203, b/33269702): temporary set it as not sanitized until - * AssistStructure automaticaly sets sanitization based on text coming from resources + * <p>Should only be set when the node is used for AutoFill purposes - it will be ignored + * when used for Assist. */ - public abstract void setSanitized(boolean sensitive); + public abstract void setSanitized(boolean sanitized); /** * Call when done populating a {@link ViewStructure} returned by diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 67d7ff8da25b..d866927b3dee 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -28,6 +28,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; import android.text.Spannable; +import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.AccessibilityClickableSpan; @@ -2421,7 +2422,7 @@ public class AccessibilityNodeInfo implements Parcelable { ClickableSpan[] spans = ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class); if (spans.length > 0) { - Spannable spannable = Spannable.Factory.getInstance().newSpannable(text); + Spannable spannable = new SpannableStringBuilder(text); for (int i = 0; i < spans.length; i++) { ClickableSpan span = spans[i]; if ((span instanceof AccessibilityClickableSpan) diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java index d9003a6fa3bd..2168444b9b58 100644 --- a/core/java/android/view/autofill/AutoFillManager.java +++ b/core/java/android/view/autofill/AutoFillManager.java @@ -179,8 +179,6 @@ public final class AutoFillManager { * @param view view whose focus changed. */ public void valueChanged(View view) { - ensureServiceClientAddedIfNeeded(); - if (!mEnabled || !mHasSession) { return; } @@ -199,8 +197,6 @@ public final class AutoFillManager { * @param value new value of the child. */ public void virtualValueChanged(View parent, int childId, AutoFillValue value) { - ensureServiceClientAddedIfNeeded(); - if (!mEnabled || !mHasSession) { return; } @@ -216,8 +212,6 @@ public final class AutoFillManager { * call this method after the form is submitted and another page is rendered. */ public void reset() { - ensureServiceClientAddedIfNeeded(); - if (!mEnabled && !mHasSession) { return; } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 6f687fe5f14c..f2c2af511f81 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -34,6 +34,7 @@ import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.autofill.AutoFillManager; import android.view.autofill.AutoFillType; import android.view.autofill.AutoFillValue; @@ -164,6 +165,10 @@ public abstract class CompoundButton extends Button implements Checkable { if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } + final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class); + if (afm != null) { + afm.valueChanged(this); + } mBroadcasting = false; } @@ -563,8 +568,6 @@ public abstract class CompoundButton extends Button implements Checkable { // TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable) - // TODO(b/33197203): override onProvideAutoFillStructure and add a change listener - @Override public void autoFill(AutoFillValue value) { if (!isEnabled()) return; @@ -579,6 +582,6 @@ public abstract class CompoundButton extends Button implements Checkable { @Override public AutoFillValue getAutoFillValue() { - return isEnabled() ? null : AutoFillValue.forToggle(isChecked()); + return isEnabled() ? AutoFillValue.forToggle(isChecked()) : null; } } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index 919c1e2c4a4c..0f0e6c30979d 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -294,14 +294,15 @@ class DayPickerView extends ViewGroup { * @param animate whether to smooth scroll to the new position * @param setSelected whether to set the specified day as selected * - * @throws IllegalArgumentException as of {@link android.os.Build.VERSION_CODES#N_MR1} if the - * provided timeInMillis is before the range start or after the range end. + * @throws IllegalArgumentException if the build version is greater than + * {@link android.os.Build.VERSION_CODES#N_MR1} and the provided timeInMillis is before + * the range start or after the range end. */ private void setDate(long timeInMillis, boolean animate, boolean setSelected) { getTempCalendarForTime(timeInMillis); final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; - if (targetSdkVersion >= N_MR1) { + if (targetSdkVersion > N_MR1) { if (mTempCalendar.before(mMinDate) || mTempCalendar.after(mMaxDate)) { throw new IllegalArgumentException("timeInMillis must be between the values of " + "getMinDate() and getMaxDate()"); diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 8ba4694c331c..76b38131477d 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -24,6 +24,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.autofill.AutoFillManager; import android.view.autofill.AutoFillType; import android.view.autofill.AutoFillValue; @@ -177,6 +178,10 @@ public class RadioGroup extends LinearLayout { if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); } + final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class); + if (afm != null) { + afm.valueChanged(this); + } } private void setCheckedStateForView(int viewId, boolean checked) { @@ -405,8 +410,6 @@ public class RadioGroup extends LinearLayout { // TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable) - // TODO(b/33197203): override onProvideAutoFillStructure and add a change listener - @Override public void autoFill(AutoFillValue value) { if (!isEnabled()) return; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 051ffee677c2..9e911632910e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -63,7 +63,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelableParcel; import android.os.SystemClock; -import android.os.UserHandle; import android.provider.Settings; import android.text.BoringLayout; import android.text.DynamicLayout; @@ -727,8 +726,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // mAutoSizeStepGranularityInPx. private boolean mHasPresetAutoSizeValues = false; - // Watcher used to notify changes to auto-fill manager. - private AutoFillChangeWatcher mAutoFillChangeWatcher; + // Indicates whether the text was set from resources or dynamically, so it can be used to + // sanitize auto-fill request. + private boolean mTextFromResource = false; /** * Kick-start the font cache for the zygote process (to pay the cost of @@ -948,6 +948,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes); int n = a.getIndexCount(); + + boolean fromResourceId = false; for (int i = 0; i < n; i++) { int attr = a.getIndex(i); @@ -1089,6 +1091,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_text: + fromResourceId = true; text = a.getText(attr); break; @@ -1567,6 +1570,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setText(text, bufferType); + if (fromResourceId) { + mTextFromResource = true; + } + if (hint != null) setHint(hint); /* @@ -5067,6 +5074,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { + mTextFromResource = false; if (text == null) { text = ""; } @@ -5301,6 +5309,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @android.view.RemotableViewMethod public final void setText(@StringRes int resid) { setText(getContext().getResources().getText(resid)); + mTextFromResource = true; } /** @@ -5327,6 +5336,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public final void setText(@StringRes int resid, BufferType type) { setText(getContext().getResources().getText(resid), type); + mTextFromResource = true; } /** @@ -9099,6 +9109,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener list.get(i).afterTextChanged(text); } } + + // Always notify AutoFillManager - it will return right away if auto-fill is disabled. + final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class); + if (afm != null) { + if (DEBUG_AUTOFILL) { + Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + text); + } + afm.valueChanged(TextView.this); + } + hideErrorIfUnchanged(); } @@ -9840,16 +9860,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** - * @return true if the user has explicitly allowed accessibility services - * to speak passwords. - */ - private boolean shouldSpeakPasswordsForAccessibility() { - return (Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, - UserHandle.USER_CURRENT_OR_SELF) == 1); - } - @Override public CharSequence getAccessibilityClassName() { return TextView.class.getName(); @@ -9872,14 +9882,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); if (forAutoFill) { - // TODO(b/33197203, b/33269702): temporary set it as not sanitized until - // AssistStructure automaticaly sets sanitization based on text coming from resources - structure.setSanitized(!isPassword); - if (mAutoFillChangeWatcher == null && isTextEditable()) { - mAutoFillChangeWatcher = new AutoFillChangeWatcher(); - addTextChangedListener(mAutoFillChangeWatcher); - // TODO(b/33197203): remove mAutoFillValueListener auto-fill session is finished - } + structure.setSanitized(mTextFromResource); } if (!isPassword || forAutoFill) { @@ -10424,13 +10427,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mHint; } - // Check whether we need to bypass the transformation - // method and expose unobscured text. - if (hasPasswordTransformationMethod() && shouldSpeakPasswordsForAccessibility()) { - return mText; - } - - // Otherwise, speak whatever text is being displayed. + // Otherwise, return whatever text is being displayed. return mTransformed; } @@ -11512,30 +11509,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - // TODO(b/33197203): implements SpanWatcher too? - private final class AutoFillChangeWatcher implements TextWatcher { - - private final AutoFillManager mAfm = mContext.getSystemService(AutoFillManager.class); - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - if (mAfm != null) { - if (DEBUG_AUTOFILL) { - Log.v(LOG_TAG, "AutoFillChangeWatcher.afterTextChanged(): s=" + s); - } - mAfm.valueChanged(TextView.this); - } - } - } - private class ChangeWatcher implements TextWatcher, SpanWatcher { private CharSequence mBeforeText; @@ -11547,9 +11520,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " before=" + before + " after=" + after + ": " + buffer); } - if (AccessibilityManager.getInstance(mContext).isEnabled() - && ((!isPasswordInputType(getInputType()) && !hasPasswordTransformationMethod()) - || shouldSpeakPasswordsForAccessibility())) { + if (AccessibilityManager.getInstance(mContext).isEnabled()) { mBeforeText = buffer.toString(); } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 4ba19f446b13..ae2e0ac88b39 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1489,21 +1489,10 @@ public class LockPatternView extends View { return bounds; } - private boolean shouldSpeakPassword() { - final boolean speakPassword = Settings.Secure.getIntForUser( - mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, - UserHandle.USER_CURRENT_OR_SELF) != 0; - final boolean hasHeadphones = mAudioManager != null ? - (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) - : false; - return speakPassword || hasHeadphones; - } - private CharSequence getTextForVirtualView(int virtualViewId) { final Resources res = getResources(); - return shouldSpeakPassword() ? res.getString( - R.string.lockscreen_access_pattern_cell_added_verbose, virtualViewId) - : res.getString(R.string.lockscreen_access_pattern_cell_added); + return res.getString(R.string.lockscreen_access_pattern_cell_added_verbose, + virtualViewId); } /** |
