diff options
Diffstat (limited to 'core/java')
66 files changed, 2025 insertions, 505 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index 224e8e9..e47d017 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -790,6 +790,31 @@ public class AnimatorInflater { return valuesArray; } + // When no value type is provided in keyframe, we need to infer the type from the value. i.e. + // if value is defined in the style of a color value, then the color type is returned. + // Otherwise, default float type is returned. + private static int inferValueTypeOfKeyframe(Resources res, Theme theme, AttributeSet attrs) { + int valueType; + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.Keyframe, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.Keyframe); + } + + TypedValue keyframeValue = a.peekValue(R.styleable.Keyframe_value); + boolean hasValue = (keyframeValue != null); + // When no value type is provided, check whether it's a color type first. + // If not, fall back to default value type (i.e. float type). + if (hasValue && isColorType(keyframeValue.type)) { + valueType = VALUE_TYPE_COLOR; + } else { + valueType = VALUE_TYPE_FLOAT; + } + a.recycle(); + return valueType; + } + private static void dumpKeyframes(Object[] keyframes, String header) { if (keyframes == null || keyframes.length == 0) { return; @@ -817,6 +842,9 @@ public class AnimatorInflater { type != XmlPullParser.END_DOCUMENT) { String name = parser.getName(); if (name.equals("keyframe")) { + if (valueType == VALUE_TYPE_UNDEFINED) { + valueType = inferValueTypeOfKeyframe(res, theme, Xml.asAttributeSet(parser)); + } Keyframe keyframe = loadKeyframe(res, theme, Xml.asAttributeSet(parser), valueType); if (keyframe != null) { if (keyframes == null) { diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index dd5f18e..f6ad847 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1095,7 +1095,8 @@ public final class AnimatorSet extends Animator { public Node clone() { try { Node node = (Node) super.clone(); - node.animation = (Animator) animation.clone(); + node.animation = animation.clone(); + node.done = false; return node; } catch (CloneNotSupportedException e) { throw new AssertionError(); diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 275e78e..292507b 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -1486,6 +1486,11 @@ public class ValueAnimator extends Animator { anim.mPaused = false; anim.mResumed = false; anim.mStartListenersCalled = false; + anim.mStartTime = 0; + anim.mStartTimeCommitted = false; + anim.mPauseTime = 0; + anim.mCurrentFraction = 0; + anim.mDelayStartTime = 0; PropertyValuesHolder[] oldValues = mValues; if (oldValues != null) { diff --git a/core/java/android/annotation/BinderThread.java b/core/java/android/annotation/BinderThread.java new file mode 100644 index 0000000..c69ba10 --- /dev/null +++ b/core/java/android/annotation/BinderThread.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated method should only be called on the binder thread. + * If the annotated element is a class, then all methods in the class should be called + * on the binder thread. + * <p> + * Example: + * <pre>{@code + * (@BinderThread + * public BeamShareData createBeamShareData() { ... } + * }</pre> + * + * {@hide} + */ +@Retention(SOURCE) +@Target({METHOD,CONSTRUCTOR,TYPE}) +public @interface BinderThread { +}
\ No newline at end of file diff --git a/core/java/android/annotation/MainThread.java b/core/java/android/annotation/MainThread.java new file mode 100644 index 0000000..18a4283 --- /dev/null +++ b/core/java/android/annotation/MainThread.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated method should only be called on the main thread. + * If the annotated element is a class, then all methods in the class should be called + * on the main thread. + * <p> + * Example: + * <pre>{@code + * @MainThread + * public void deliverResult(D data) { ... } + * }</pre> + * + * {@hide} + */ +@Retention(SOURCE) +@Target({METHOD,CONSTRUCTOR,TYPE}) +public @interface MainThread { +}
\ No newline at end of file diff --git a/core/java/android/annotation/UiThread.java b/core/java/android/annotation/UiThread.java new file mode 100644 index 0000000..b814600 --- /dev/null +++ b/core/java/android/annotation/UiThread.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated method or constructor should only be called on the UI thread. + * If the annotated element is a class, then all methods in the class should be called + * on the UI thread. + * <p> + * Example: + * <pre>{@code + * @UiThread + * public abstract void setText(@NonNull String text) { ... } + * }</pre> + * + * {@hide} + */ +@Retention(SOURCE) +@Target({METHOD,CONSTRUCTOR,TYPE}) +public @interface UiThread { +}
\ No newline at end of file diff --git a/core/java/android/annotation/WorkerThread.java b/core/java/android/annotation/WorkerThread.java new file mode 100644 index 0000000..dd12e05 --- /dev/null +++ b/core/java/android/annotation/WorkerThread.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated method should only be called on a worker thread. + * If the annotated element is a class, then all methods in the class should be called + * on a worker thread. + * <p> + * Example: + * <pre>{@code + * (@WorkerThread + * protected abstract FilterResults performFiltering(CharSequence constraint); + * }</pre> + * + * {@hide} + */ +@Retention(SOURCE) +@Target({METHOD,CONSTRUCTOR,TYPE}) +public @interface WorkerThread { +}
\ No newline at end of file diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4ccde1c..69cba78 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6483,6 +6483,18 @@ public class Activity extends ContextThemeWrapper } /** + * Shows the user the system defined message for telling the user how to exit + * lock task mode. The task containing this activity must be in lock task mode at the time + * of this call for the message to be displayed. + */ + public void showLockTaskEscapeMessage() { + try { + ActivityManagerNative.getDefault().showLockTaskEscapeMessage(mToken); + } catch (RemoteException e) { + } + } + + /** * Interface for informing a translucent {@link Activity} once all visible activities below it * have completed drawing. This is necessary only after an {@link Activity} has been made * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d56dc1e..bde8f39 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -16,6 +16,8 @@ package android.app; +import android.annotation.NonNull; + /** * Activity manager local system service interface. * @@ -27,4 +29,23 @@ public abstract class ActivityManagerInternal { public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs, String processName, String abiOverride, int uid, Runnable crashHandler); + + /** + * Acquires a sleep token with the specified tag. + * + * @param tag A string identifying the purpose of the token (eg. "Dream"). + */ + public abstract SleepToken acquireSleepToken(@NonNull String tag); + + /** + * Sleep tokens cause the activity manager to put the top activity to sleep. + * They are used by components such as dreams that may hide and block interaction + * with underlying activities. + */ + public static abstract class SleepToken { + /** + * Releases the sleep token. + */ + public abstract void release(); + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index add7af2..b11c509 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2345,6 +2345,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final IBinder token = data.readStrongBinder(); + showLockTaskEscapeMessage(token); + reply.writeNoException(); + return true; + } + case SET_TASK_DESCRIPTION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -2505,6 +2513,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case UPDATE_DEVICE_OWNER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String packageName = data.readString(); + updateDeviceOwner(packageName); + reply.writeNoException(); + return true; + } + case GET_PACKAGE_PROCESS_STATE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String pkg = data.readString(); @@ -5552,6 +5568,19 @@ class ActivityManagerProxy implements IActivityManager } @Override + public void showLockTaskEscapeMessage(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION, data, reply, + IBinder.FLAG_ONEWAY); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values) throws RemoteException { Parcel data = Parcel.obtain(); @@ -5780,6 +5809,18 @@ class ActivityManagerProxy implements IActivityManager } @Override + public void updateDeviceOwner(String packageName) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(packageName); + mRemote.transact(UPDATE_DEVICE_OWNER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override public int getPackageProcessState(String packageName) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 10d6d01..ab5f811 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2541,11 +2541,16 @@ public final class ActivityThread { if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) { data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity)); AssistContent content = new AssistContent(); - Intent intent = new Intent(r.activity.getIntent()); - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); - intent.removeUnsafeExtras(); - content.setIntent(intent); + Intent activityIntent = r.activity.getIntent(); + if (activityIntent != null) { + Intent intent = new Intent(activityIntent); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + intent.removeUnsafeExtras(); + content.setIntent(intent); + } else { + content.setIntent(new Intent()); + } r.activity.onProvideAssistContent(content); data.putParcelable(AssistContent.ASSIST_KEY, content); } diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 179957d..9d1d312 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -597,6 +597,15 @@ public class AlarmManager } } + /** @hide */ + public long getNextWakeFromIdleTime() { + try { + return mService.getNextWakeFromIdleTime(); + } catch (RemoteException ex) { + return Long.MAX_VALUE; + } + } + /** * Gets information about the next alarm clock currently scheduled. * diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java index 1e159a3..9946d79 100644 --- a/core/java/android/app/AssistStructure.java +++ b/core/java/android/app/AssistStructure.java @@ -218,6 +218,7 @@ final public class AssistStructure implements Parcelable { static final int FLAGS_FOCUSED = 0x00000020; static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000; static final int FLAGS_SELECTED = 0x00000040; + static final int FLAGS_ASSIST_BLOCKED = 0x00000080; static final int FLAGS_ACTIVATED = 0x40000000; static final int FLAGS_CHECKABLE = 0x00000100; static final int FLAGS_CHECKED = 0x00000200; @@ -356,6 +357,10 @@ final public class AssistStructure implements Parcelable { return mFlags&ViewNode.FLAGS_VISIBILITY_MASK; } + public boolean isAssistBlocked() { + return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0; + } + public boolean isEnabled() { return (mFlags&ViewNode.FLAGS_DISABLED) == 0; } @@ -484,6 +489,12 @@ final public class AssistStructure implements Parcelable { } @Override + public void setAssistBlocked(boolean state) { + mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED) + | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED); + } + + @Override public void setEnabled(boolean state) { mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED) | (state ? 0 : ViewNode.FLAGS_DISABLED); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 59de281..00558fe 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -467,6 +467,8 @@ public interface IActivityManager extends IInterface { public int getLockTaskModeState() throws RemoteException; + public void showLockTaskEscapeMessage(IBinder token) throws RemoteException; + public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values) throws RemoteException; public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException; @@ -493,6 +495,7 @@ public interface IActivityManager extends IInterface { public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) throws RemoteException; public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException; + public void updateDeviceOwner(String packageName) throws RemoteException; public int getPackageProcessState(String packageName) throws RemoteException; @@ -834,4 +837,6 @@ public interface IActivityManager extends IInterface { int NOTE_ALARM_START_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+291; int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292; int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293; + int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294; + int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295; } diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index d5719f5..327c00b 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -33,6 +33,7 @@ interface IAlarmManager { boolean setTime(long millis); void setTimeZone(String zone); void remove(in PendingIntent operation); + long getNextWakeFromIdleTime(); AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 913159a..e275df0 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -89,4 +89,9 @@ interface INotificationManager boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); + + byte[] getBackupPayload(int user); + void applyRestore(in byte[] payload, int user); + + ParceledListSlice getAppActiveNotifications(String callingPkg, int userId); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 7133dce..0a59026 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -23,6 +23,7 @@ import android.app.Notification.Builder; import android.app.NotificationManager.Policy.Token; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -35,10 +36,12 @@ import android.os.StrictMode; import android.os.UserHandle; import android.provider.Settings.Global; import android.service.notification.IConditionListener; +import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; import android.util.Log; import java.util.Objects; +import java.util.List; /** * Class to notify the user of events that happen. This is how you tell @@ -642,4 +645,30 @@ public class NotificationManager } } + /** + * Recover a list of active notifications: ones that have been posted by the calling app that + * have not yet been dismissed by the user or {@link #cancel(String, int)}ed by the app. + * + * Each notification is embedded in a {@link StatusBarNotification} object, including the + * original <code>tag</code> and <code>id</code> supplied to + * {@link #notify(String, int, Notification) notify()} + * (via {@link StatusBarNotification#getTag() getTag()} and + * {@link StatusBarNotification#getId() getId()}) as well as a copy of the original + * {@link Notification} object (via {@link StatusBarNotification#getNotification()}). + * + * @return An array of {@link StatusBarNotification}. + */ + public StatusBarNotification[] getActiveNotifications() { + final INotificationManager service = getService(); + final String pkg = mContext.getPackageName(); + try { + final ParceledListSlice<StatusBarNotification> parceledList + = service.getAppActiveNotifications(pkg, UserHandle.myUserId()); + final List<StatusBarNotification> list = parceledList.getList(); + return list.toArray(new StatusBarNotification[list.size()]); + } catch (RemoteException e) { + Log.e(TAG, "Unable to talk to notification manager. Woe!", e); + } + return new StatusBarNotification[0]; + } } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index fe284ce..aea413d 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -264,6 +264,20 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE = "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE"; /** + * Broadcast action: notify device owner that there is a pending system update. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE = "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE"; + + /** + * A long type extra for {@link #onSystemUpdatePending} recording the system time as given by + * {@link System#currentTimeMillis()} when the current pending system update is first available. + * @hide + */ + public static final String EXTRA_SYSTEM_UPDATE_RECEIVED_TIME = "android.app.extra.SYSTEM_UPDATE_RECEIVED_TIME"; + + /** * Name under which a DevicePolicy component publishes information * about itself. This meta-data must reference an XML resource containing * a device-admin tag. @@ -486,6 +500,22 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** + * Allows the receiver to be notified when information about a pending system update is + * available from the system update service. The same pending system update can trigger multiple + * calls to this method, so it is necessary to examine the incoming parameters for details about + * the update. + * <p> + * This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param receivedTime The time as given by {@link System#currentTimeMillis()} indicating when + * the current pending update was first available. -1 if no pending update is available. + */ + public void onSystemUpdatePending(Context context, Intent intent, long receivedTime) { + } + + /** * Intercept standard device administrator broadcasts. Implementations * should not override this method; it is better to implement the * convenience callbacks for each action. @@ -530,6 +560,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onLockTaskModeExiting(context, intent); } else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) { onReadyForUserInitialization(context, intent); + } else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) { + long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1); + onSystemUpdatePending(context, intent, receivedTime); } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0ea237a..a20aa66 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -800,11 +800,12 @@ public class DevicePolicyManager { public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; /** - * Broadcast action: notify that a new local OTA policy has been set by the device owner. - * The new policy can be retrieved by {@link #getOtaPolicy()}. + * Broadcast action: notify that a new local system update policy has been set by the device + * owner. The new policy can be retrieved by {@link #getSystemUpdatePolicy()}. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_OTA_POLICY_CHANGED = "android.app.action.OTA_POLICY_CHANGED"; + public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED + = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"; /** @@ -1634,6 +1635,23 @@ public class DevicePolicyManager { } /** + * Queries whether {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set. + * + * @return true if DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set. + * @hide + */ + public boolean getDoNotAskCredentialsOnBoot() { + if (mService != null) { + try { + return mService.getDoNotAskCredentialsOnBoot(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to call getDoNotAskCredentialsOnBoot()", e); + } + } + return false; + } + + /** * Setting this to a value greater than zero enables a built-in policy * that will perform a device wipe after too many incorrect * device-unlock passwords have been entered. This built-in policy combines @@ -1710,6 +1728,16 @@ public class DevicePolicyManager { public static final int RESET_PASSWORD_REQUIRE_ENTRY = 0x0001; /** + * Flag for {@link #resetPassword}: don't ask for user credentials on device boot. + * If the flag is set, the device can be booted without asking for user password. + * The absence of this flag does not change the current boot requirements. This flag + * can be set by the device owner only. If the app is not the device owner, the flag + * is ignored. Once the flag is set, it cannot be reverted back without resetting the + * device to factory defaults. + */ + public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002; + + /** * Force a new device unlock password (the password needed to access the * entire device, not for individual accounts) on the user. This takes * effect immediately. @@ -1732,7 +1760,8 @@ public class DevicePolicyManager { * <p>Calling this from a managed profile will throw a security exception. * * @param password The new password for the user. Null or empty clears the password. - * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}. + * @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and + * {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT}. * @return Returns true if the password was applied, or false if it is * not acceptable for the current constraints. */ @@ -4189,46 +4218,46 @@ public class DevicePolicyManager { } } - /* - * Called by device owners to set a local OTA update policy. When a new OTA policy is set, - * {@link #ACTION_OTA_POLICY_CHANGED} is broadcasted. + /** + * Called by device owners to set a local system update policy. When a new policy is set, + * {@link #ACTION_SYSTEM_UPDATE_POLICY_CHANGED} is broadcasted. * * @param who Which {@link DeviceAdminReceiver} this request is associated with. All components - * in the device owner package can set OTA policies and the most recent policy takes effect. - * @param policy the new OTA policy, or null to clear the current policy. - * @see OtaPolicy + * in the device owner package can set system update policies and the most recent policy takes + * effect. + * @param policy the new policy, or null to clear the current policy. + * @see SystemUpdatePolicy */ - public void setOtaPolicy(ComponentName who, OtaPolicy policy) { + public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { if (mService != null) { try { if (policy != null) { - mService.setOtaPolicy(who, policy.getPolicyBundle()); + mService.setSystemUpdatePolicy(who, policy.getPolicyBundle()); } else { - mService.setOtaPolicy(who, null); + mService.setSystemUpdatePolicy(who, null); } } catch (RemoteException re) { - Log.w(TAG, "Error calling setOtaPolicy", re); + Log.w(TAG, "Error calling setSystemUpdatePolicy", re); } } } /** - * Retrieve a local OTA update policy set previously by {@link #setOtaPolicy}. + * Retrieve a local system update policy set previously by {@link #setSystemUpdatePolicy}. * - * @return The current OTA policy object, or null if no policy is set or the system does not - * support managed OTA. + * @return The current policy object, or null if no policy is set. */ - public OtaPolicy getOtaPolicy() { + public SystemUpdatePolicy getSystemUpdatePolicy() { if (mService != null) { try { - PersistableBundle bundle = mService.getOtaPolicy(); + PersistableBundle bundle = mService.getSystemUpdatePolicy(); if (bundle != null) { - return new OtaPolicy(bundle); + return new SystemUpdatePolicy(bundle); } else { return null; } } catch (RemoteException re) { - Log.w(TAG, "Error calling getOtaPolicy", re); + Log.w(TAG, "Error calling getSystemUpdatePolicy", re); } } return null; @@ -4272,4 +4301,24 @@ public class DevicePolicyManager { Log.w(TAG, "Failed talking with device policy service", re); } } + + /** + * Callable by the system update service to notify device owners about pending updates. + * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE} + * permission. + * + * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()} indicating + * when the current pending update was first available. -1 if no update is available. + * @hide + */ + @SystemApi + public void notifyPendingSystemUpdate(long updateReceivedTime) { + if (mService != null) { + try { + mService.notifyPendingSystemUpdate(updateReceivedTime); + } catch (RemoteException re) { + Log.w(TAG, "Could not notify device owner about pending system update", re); + } + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 7502e1d..087fc88 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -219,9 +219,12 @@ interface IDevicePolicyManager { void setUserIcon(in ComponentName admin, in Bitmap icon); void sendDeviceInitializerStatus(int statusCode, String description); - void setOtaPolicy(in ComponentName who, in PersistableBundle policy); - PersistableBundle getOtaPolicy(); + void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy); + PersistableBundle getSystemUpdatePolicy(); boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled); void setStatusBarEnabledState(in ComponentName who, boolean enabled); + boolean getDoNotAskCredentialsOnBoot(); + + void notifyPendingSystemUpdate(in long updateReceivedTime); } diff --git a/core/java/android/app/admin/OtaPolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index 98581a7..de56cd0 100644 --- a/core/java/android/app/admin/OtaPolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -23,12 +23,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * A class that represents a local OTA policy set by the device owner. + * A class that represents a local system update policy set by the device owner. * - * @see DevicePolicyManager#setOtaPolicy - * @see DevicePolicyManager#getOtaPolicy + * @see DevicePolicyManager#setSystemUpdatePolicy + * @see DevicePolicyManager#getSystemUpdatePolicy */ -public class OtaPolicy { +public class SystemUpdatePolicy { /** @hide */ @IntDef({ @@ -36,22 +36,27 @@ public class OtaPolicy { TYPE_INSTALL_WINDOWED, TYPE_POSTPONE}) @Retention(RetentionPolicy.SOURCE) - @interface OtaPolicyType {} + @interface SystemUpdatePolicyType {} /** - * Install OTA update automatically as soon as one is available. + * Install system update automatically as soon as one is available. */ public static final int TYPE_INSTALL_AUTOMATIC = 1; /** - * Install OTA update automatically within a daily maintenance window, for a maximum of two-week - * period. After that period the OTA will be installed automatically. + * Install system update automatically within a daily maintenance window, for a maximum of 30 + * days. After the expiration the policy will no longer be effective and the system should + * revert back to its normal behavior as if no policy were set. The only exception is + * {@link #TYPE_INSTALL_AUTOMATIC} which should still take effect to install system update + * immediately. */ public static final int TYPE_INSTALL_WINDOWED = 2; /** - * Incoming OTA will be blocked for a maximum of two weeks, after which it will be installed - * automatically. + * Incoming system update will be blocked for a maximum of 30 days, after which the system + * should revert back to its normal behavior as if no policy were set. The only exception is + * {@link #TYPE_INSTALL_AUTOMATIC} which should still take effect to install system update + * immediately. */ public static final int TYPE_POSTPONE = 3; @@ -61,15 +66,15 @@ public class OtaPolicy { private PersistableBundle mPolicy; - public OtaPolicy() { + public SystemUpdatePolicy() { mPolicy = new PersistableBundle(); } /** - * Construct an OtaPolicy object from a bundle. + * Construct an SystemUpdatePolicy object from a bundle. * @hide */ - public OtaPolicy(PersistableBundle in) { + public SystemUpdatePolicy(PersistableBundle in) { mPolicy = new PersistableBundle(in); } @@ -82,7 +87,9 @@ public class OtaPolicy { } /** - * Set the OTA policy to: install OTA update automatically as soon as one is available. + * Set the policy to: install update automatically as soon as one is available. + * + * @see #TYPE_INSTALL_AUTOMATIC */ public void setAutomaticInstallPolicy() { mPolicy.clear(); @@ -90,18 +97,19 @@ public class OtaPolicy { } /** - * Set the OTA policy to: new OTA update will only be installed automatically when the system + * Set the policy to: new system update will only be installed automatically when the system * clock is inside a daily maintenance window. If the start and end times are the same, the - * window is considered to include the WHOLE 24 hours, that is, OTAs can install at any time. If - * the given window in invalid, a {@link OtaPolicy.InvalidWindowException} will be thrown. If - * start time is later than end time, the window is considered spanning midnight, i.e. end time - * donates a time on the next day. The maintenance window will last for two weeks, after which - * the OTA will be installed automatically. + * window is considered to include the WHOLE 24 hours, that is, updates can install at any time. + * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be + * thrown. If start time is later than end time, the window is considered spanning midnight, + * i.e. end time donates a time on the next day. The maintenance window will last for 30 days, + * after which the system should revert back to its normal behavior as if no policy were set. * * @param startTime the start of the maintenance window, measured as the number of minutes from - * midnight in the device's local time. Must be in the range of [0, 1440). + * midnight in the device's local time. Must be in the range of [0, 1440). * @param endTime the end of the maintenance window, measured as the number of minutes from - * midnight in the device's local time. Must be in the range of [0, 1440). + * midnight in the device's local time. Must be in the range of [0, 1440). + * @see #TYPE_INSTALL_WINDOWED */ public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{ if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) { @@ -114,8 +122,10 @@ public class OtaPolicy { } /** - * Set the OTA policy to: block installation for a maximum period of two weeks. After the - * block expires the OTA will be installed automatically. + * Set the policy to: block installation for a maximum period of 30 days. After expiration the + * system should revert back to its normal behavior as if no policy were set. + * + * @see #TYPE_POSTPONE */ public void setPostponeInstallPolicy() { mPolicy.clear(); @@ -123,12 +133,12 @@ public class OtaPolicy { } /** - * Returns the type of OTA policy. + * Returns the type of system update policy. * * @return an integer, either one of {@link #TYPE_INSTALL_AUTOMATIC}, * {@link #TYPE_INSTALL_WINDOWED} and {@link #TYPE_POSTPONE}, or -1 if no policy has been set. */ - @OtaPolicyType + @SystemUpdatePolicyType public int getPolicyType() { return mPolicy.getInt(KEY_POLICY_TYPE, -1); } @@ -167,7 +177,7 @@ public class OtaPolicy { } /** - * Exception thrown by {@link OtaPolicy#setWindowedInstallPolicy(int, int)} in case the + * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the * specified window is invalid. */ public static class InvalidWindowException extends Exception { diff --git a/core/java/android/app/backup/BlobBackupHelper.java b/core/java/android/app/backup/BlobBackupHelper.java new file mode 100644 index 0000000..cdc62dc --- /dev/null +++ b/core/java/android/app/backup/BlobBackupHelper.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.backup; + +import android.os.ParcelFileDescriptor; +import android.util.ArrayMap; +import android.util.Log; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.CRC32; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Utility class for writing BackupHelpers whose underlying data is a + * fixed set of byte-array blobs. The helper manages diff detection + * and compression on the wire. + * + * @hide + */ +public abstract class BlobBackupHelper implements BackupHelper { + private static final String TAG = "BlobBackupHelper"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final int mCurrentBlobVersion; + private final String[] mKeys; + + public BlobBackupHelper(int currentBlobVersion, String... keys) { + mCurrentBlobVersion = currentBlobVersion; + mKeys = keys; + } + + // Client interface + + /** + * Generate and return the byte array containing the backup payload describing + * the current data state. During a backup operation this method is called once + * per key that was supplied to the helper's constructor. + * + * @return A byte array containing the data blob that the caller wishes to store, + * or {@code null} if the current state is empty or undefined. + */ + abstract protected byte[] getBackupPayload(String key); + + /** + * Given a byte array that was restored from backup, do whatever is appropriate + * to apply that described state in the live system. This method is called once + * per key/value payload that was delivered for restore. Typically data is delivered + * for restore in lexical order by key, <i>not</i> in the order in which the keys + * were supplied in the constructor. + * + * @param payload The byte array that was passed to {@link #getBackupPayload()} + * on the ancestral device. + */ + abstract protected void applyRestoredPayload(String key, byte[] payload); + + + // Internal implementation + + /* + * State on-disk format: + * [Int] : overall blob version number + * [Int=N] : number of keys represented in the state blob + * N* : + * [String] key + * [Long] blob checksum, calculated after compression + */ + @SuppressWarnings("resource") + private ArrayMap<String, Long> readOldState(ParcelFileDescriptor oldStateFd) { + final ArrayMap<String, Long> state = new ArrayMap<String, Long>(); + + FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); + BufferedInputStream bis = new BufferedInputStream(fis); + DataInputStream in = new DataInputStream(bis); + + try { + int version = in.readInt(); + if (version <= mCurrentBlobVersion) { + final int numKeys = in.readInt(); + for (int i = 0; i < numKeys; i++) { + String key = in.readUTF(); + long checksum = in.readLong(); + state.put(key, checksum); + } + } else { + Log.w(TAG, "Prior state from unrecognized version " + version); + } + } catch (EOFException e) { + // Empty file is expected on first backup, so carry on. If the state + // is truncated we just treat it the same way. + state.clear(); + } catch (Exception e) { + Log.e(TAG, "Error examining prior backup state " + e.getMessage()); + state.clear(); + } + + return state; + } + + /** + * New overall state record + */ + private void writeBackupState(ArrayMap<String, Long> state, ParcelFileDescriptor stateFile) { + try { + FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); + + // We explicitly don't close 'out' because we must not close the backing fd. + // The FileOutputStream will not close it implicitly. + @SuppressWarnings("resource") + DataOutputStream out = new DataOutputStream(fos); + + out.writeInt(mCurrentBlobVersion); + + final int N = (state != null) ? state.size() : 0; + out.writeInt(N); + for (int i = 0; i < N; i++) { + out.writeUTF(state.keyAt(i)); + out.writeLong(state.valueAt(i).longValue()); + } + } catch (IOException e) { + Log.e(TAG, "Unable to write updated state", e); + } + } + + // Also versions the deflated blob internally in case we need to revise it + private byte[] deflate(byte[] data) { + byte[] result = null; + if (data != null) { + try { + ByteArrayOutputStream sink = new ByteArrayOutputStream(); + DataOutputStream headerOut = new DataOutputStream(sink); + + // write the header directly to the sink ahead of the deflated payload + headerOut.writeInt(mCurrentBlobVersion); + + DeflaterOutputStream out = new DeflaterOutputStream(sink); + out.write(data); + out.close(); // finishes and commits the compression run + result = sink.toByteArray(); + if (DEBUG) { + Log.v(TAG, "Deflated " + data.length + " bytes to " + result.length); + } + } catch (IOException e) { + Log.w(TAG, "Unable to process payload: " + e.getMessage()); + } + } + return result; + } + + // Returns null if inflation failed + private byte[] inflate(byte[] compressedData) { + byte[] result = null; + if (compressedData != null) { + try { + ByteArrayInputStream source = new ByteArrayInputStream(compressedData); + DataInputStream headerIn = new DataInputStream(source); + int version = headerIn.readInt(); + if (version > mCurrentBlobVersion) { + Log.w(TAG, "Saved payload from unrecognized version " + version); + return null; + } + + InflaterInputStream in = new InflaterInputStream(source); + ByteArrayOutputStream inflated = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int nRead; + while ((nRead = in.read(buffer)) > 0) { + inflated.write(buffer, 0, nRead); + } + in.close(); + inflated.flush(); + result = inflated.toByteArray(); + if (DEBUG) { + Log.v(TAG, "Inflated " + compressedData.length + " bytes to " + result.length); + } + } catch (IOException e) { + // result is still null here + Log.w(TAG, "Unable to process restored payload: " + e.getMessage()); + } + } + return result; + } + + private long checksum(byte[] buffer) { + if (buffer != null) { + try { + CRC32 crc = new CRC32(); + ByteArrayInputStream bis = new ByteArrayInputStream(buffer); + byte[] buf = new byte[4096]; + int nRead = 0; + while ((nRead = bis.read(buf)) >= 0) { + crc.update(buf, 0, nRead); + } + return crc.getValue(); + } catch (Exception e) { + // whoops; fall through with an explicitly bogus checksum + } + } + return -1; + } + + // BackupHelper interface + + @Override + public void performBackup(ParcelFileDescriptor oldStateFd, BackupDataOutput data, + ParcelFileDescriptor newStateFd) { + + final ArrayMap<String, Long> oldState = readOldState(oldStateFd); + final ArrayMap<String, Long> newState = new ArrayMap<String, Long>(); + + try { + for (String key : mKeys) { + final byte[] payload = deflate(getBackupPayload(key)); + final long checksum = checksum(payload); + newState.put(key, checksum); + + Long oldChecksum = oldState.get(key); + if (oldChecksum == null || checksum != oldChecksum) { + if (DEBUG) { + Log.i(TAG, "State has changed for key " + key + ", writing"); + } + if (payload != null) { + data.writeEntityHeader(key, payload.length); + data.writeEntityData(payload, payload.length); + } else { + // state's changed but there's no current payload => delete + data.writeEntityHeader(key, -1); + } + } else { + if (DEBUG) { + Log.i(TAG, "No change under key " + key + " => not writing"); + } + } + } + } catch (Exception e) { + Log.w(TAG, "Unable to record notification state: " + e.getMessage()); + newState.clear(); + } finally { + // Always recommit the state even if nothing changed + writeBackupState(newState, newStateFd); + } + } + + @Override + public void restoreEntity(BackupDataInputStream data) { + final String key = data.getKey(); + try { + // known key? + int which; + for (which = 0; which < mKeys.length; which++) { + if (key.equals(mKeys[which])) { + break; + } + } + if (which >= mKeys.length) { + Log.e(TAG, "Unrecognized key " + key + ", ignoring"); + return; + } + + byte[] compressed = new byte[data.size()]; + data.read(compressed); + byte[] payload = inflate(compressed); + applyRestoredPayload(key, payload); + } catch (Exception e) { + Log.e(TAG, "Exception restoring entity " + key + " : " + e.getMessage()); + } + } + + @Override + public void writeNewStateDescription(ParcelFileDescriptor newState) { + // Just ensure that we do a full backup the first time after a restore + writeBackupState(null, newState); + } +} diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 393cf8e..fd65d56 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -26,6 +26,7 @@ import android.content.pm.ProviderInfo; import android.content.res.AssetFileDescriptor; import android.content.res.Configuration; import android.database.Cursor; +import android.database.MatrixCursor; import android.database.SQLException; import android.net.Uri; import android.os.AsyncTask; @@ -204,8 +205,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 { validateIncomingUri(uri); uri = getUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { - return rejectQuery(uri, projection, selection, selectionArgs, sortOrder, - CancellationSignal.fromTransport(cancellationSignal)); + // The caller has no access to the data, so return an empty cursor with + // the columns in the requested order. The caller may ask for an invalid + // column and we would not catch that but this is not a problem in practice. + // We do not call ContentProvider#query with a modified where clause since + // the implementation is not guaranteed to be backed by a SQL database, hence + // it may not handle properly the tautology where clause we would have created. + return new MatrixCursor(projection, 0); } final String original = setCallingPackage(callingPkg); try { @@ -817,31 +823,6 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } /** - * @hide - * Implementation when a caller has performed a query on the content - * provider, but that call has been rejected for the operation given - * to {@link #setAppOps(int, int)}. The default implementation - * rewrites the <var>selection</var> argument to include a condition - * that is never true (so will always result in an empty cursor) - * and calls through to {@link #query(android.net.Uri, String[], String, String[], - * String, android.os.CancellationSignal)} with that. - */ - public Cursor rejectQuery(Uri uri, String[] projection, - String selection, String[] selectionArgs, String sortOrder, - CancellationSignal cancellationSignal) { - // The read is not allowed... to fake it out, we replace the given - // selection statement with a dummy one that will always be false. - // This way we will get a cursor back that has the correct structure - // but contains no rows. - if (selection == null || selection.isEmpty()) { - selection = "'A' = 'B'"; - } else { - selection = "'A' = 'B' AND (" + selection + ")"; - } - return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal); - } - - /** * Implement this to handle query requests from clients. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3bf3f85..5eacce3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3098,7 +3098,6 @@ public abstract class Context { * {@link android.media.midi.MidiManager} for accessing the MIDI service. * * @see #getSystemService - * @hide */ public static final String MIDI_SERVICE = "midi"; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index e2701ee..6c32873 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -339,8 +339,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use * cleartext network traffic, in which case platform components (e.g., HTTP stacks, - * {@code WebView}, {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to - * use cleartext traffic. Third-party libraries are encouraged to honor this flag as well. + * {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext + * traffic. Third-party libraries are encouraged to honor this flag as well. + * + * <p>NOTE: {@code WebView} does not honor this flag. + * + * <p>This flag comes from + * {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic + * android:usesCleartextTraffic} of the <application> tag. */ public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27; diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index c81517a..5c21c8e 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -146,6 +146,7 @@ public class LauncherApps { try { activities = mService.getLauncherActivities(packageName, user); } catch (RemoteException re) { + throw new RuntimeException("Failed to call LauncherAppsService"); } if (activities == null) { return Collections.EMPTY_LIST; diff --git a/core/java/android/content/pm/PackageCleanItem.java b/core/java/android/content/pm/PackageCleanItem.java index b1896aa..e1656d6 100644 --- a/core/java/android/content/pm/PackageCleanItem.java +++ b/core/java/android/content/pm/PackageCleanItem.java @@ -20,7 +20,7 @@ import android.os.Parcel; import android.os.Parcelable; /** @hide */ -public class PackageCleanItem { +public class PackageCleanItem implements Parcelable { public final int userId; public final String packageName; public final boolean andCode; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a0cec50..e4108b1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1574,6 +1574,21 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: This is a device dedicated to showing UI + * on a vehicle headunit. A headunit here is defined to be inside a + * vehicle that may or may not be moving. A headunit uses either a + * primary display in the center console and/or additional displays in + * the instrument cluster or elsewhere in the vehicle. Headunit display(s) + * have limited size and resolution. The user will likely be focused on + * driving so limiting driver distraction is a primary concern. User input + * can be a variety of hard buttons, touch, rotary controllers and even mouse- + * like interfaces. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: This is a device dedicated to showing UI * on a television. Television here is defined to be a typical living * room television experience: displayed on a big screen, where the user * is sitting far away from it, and the dominant form of input will be diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java index 335a45e..e5c2203 100644 --- a/core/java/android/content/pm/ParceledListSlice.java +++ b/core/java/android/content/pm/ParceledListSlice.java @@ -55,6 +55,7 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable { mList = list; } + @SuppressWarnings("unchecked") private ParceledListSlice(Parcel p, ClassLoader loader) { final int N = p.readInt(); mList = new ArrayList<T>(N); @@ -63,7 +64,7 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable { return; } - Parcelable.Creator<T> creator = p.readParcelableCreator(loader); + Parcelable.Creator<?> creator = p.readParcelableCreator(loader); Class<?> listElementClass = null; int i = 0; diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 24a7d33..3cda39a 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -91,8 +91,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private static final Pattern TRIM_SQL_PATTERN = Pattern.compile("[\\s]*\\n+[\\s]*"); - private final CloseGuard mCloseGuard = CloseGuard.get(); private final SQLiteConnectionPool mPool; @@ -1203,7 +1201,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } private static String trimSqlForDisplay(String sql) { - return TRIM_SQL_PATTERN.matcher(sql).replaceAll(" "); + // Note: Creating and caching a regular expression is expensive at preload-time + // and stops compile-time initialization. This pattern is only used when + // dumping the connection, which is a rare (mainly error) case. So: + // DO NOT CACHE. + return sql.replaceAll("[\\s]*\\n+[\\s]*", " "); } /** @@ -1437,9 +1439,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } private static final class Operation { - private static final SimpleDateFormat sDateFormat = - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - public long mStartTime; public long mEndTime; public String mKind; @@ -1494,7 +1493,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } private String getFormattedStartTime() { - return sDateFormat.format(new Date(mStartTime)); + // Note: SimpleDateFormat is not thread-safe, cannot be compile-time created, and is + // relatively expensive to create during preloading. This method is only used + // when dumping a connection, which is a rare (mainly error) case. So: + // DO NOT CACHE. + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(mStartTime)); } } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 0d7b261..2257b0a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -587,6 +587,25 @@ public class FingerprintManager { return false; } + /** + * Retrieves the authenticator token for binding keys to the lifecycle + * of the current set of fingerprints. Used only by internal clients. + * + * @hide + */ + public long getAuthenticatorId() { + if (mService != null) { + try { + return mService.getAuthenticatorId(); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e); + } + } else { + Log.w(TAG, "getAuthenticatorId(): Service not connected!"); + } + return 0; + } + private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch(msg.what) { @@ -792,4 +811,5 @@ public class FingerprintManager { } }; -}
\ No newline at end of file +} + diff --git a/core/java/android/hardware/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java index ae3d4a4..525f254 100644 --- a/core/java/android/hardware/fingerprint/FingerprintUtils.java +++ b/core/java/android/hardware/fingerprint/FingerprintUtils.java @@ -17,6 +17,8 @@ package android.hardware.fingerprint; import android.content.ContentResolver; +import android.content.Context; +import android.os.Vibrator; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -35,6 +37,8 @@ public class FingerprintUtils { private static final boolean DEBUG = true; private static final String TAG = "FingerprintUtils"; + private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30}; + private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30}; private static int[] toIntArray(List<Integer> list) { if (list == null) { @@ -104,5 +108,19 @@ class FingerprintUtils { return false; } + public static void vibrateFingerprintError(Context context) { + Vibrator vibrator = context.getSystemService(Vibrator.class); + if (vibrator != null) { + vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1); + } + } + + public static void vibrateFingerprintSuccess(Context context) { + Vibrator vibrator = context.getSystemService(Vibrator.class); + if (vibrator != null) { + vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1); + } + } + }; diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 51a0e4c..c5ec08c 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -63,4 +63,6 @@ interface IFingerprintService { // Gets the unique device id for hardware enumerated at i // long getHardwareDevice(int i); + // Gets the authenticator ID for fingerprint + long getAuthenticatorId(); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index e75f337..63f48cf 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -98,16 +98,6 @@ public class ConnectivityManager { public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; /** - * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any - * historic {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}. - * - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String CONNECTIVITY_ACTION_IMMEDIATE = - "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE"; - - /** * The lookup key for a {@link NetworkInfo} object. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * @@ -315,6 +305,9 @@ public class ConnectivityManager { * same network interface as {@link #TYPE_MOBILE} or it may use a different * one. This is used by applications needing to talk to the carrier's * Multimedia Messaging Service servers. + * + * @deprecated Applications should instead use {@link #requestNetwork} to request a network that + * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability. */ public static final int TYPE_MOBILE_MMS = 2; /** @@ -322,6 +315,9 @@ public class ConnectivityManager { * same network interface as {@link #TYPE_MOBILE} or it may use a different * one. This is used by applications needing to talk to the carrier's * Secure User Plane Location servers for help locating the device. + * + * @deprecated Applications should instead use {@link #requestNetwork} to request a network that + * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability. */ public static final int TYPE_MOBILE_SUPL = 3; /** @@ -334,9 +330,10 @@ public class ConnectivityManager { /** * A High Priority Mobile data connection. This network type uses the * same network interface as {@link #TYPE_MOBILE} but the routing setup - * is different. Only requesting processes will have access to the - * Mobile DNS servers and only IP's explicitly requested via {@link #requestRouteToHost} - * will route over this interface if no default route exists. + * is different. + * + * @deprecated Applications should instead use {@link #requestNetwork} to request a network that + * uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport. */ public static final int TYPE_MOBILE_HIPRI = 5; /** @@ -396,7 +393,7 @@ public class ConnectivityManager { */ public static final int TYPE_MOBILE_IA = 14; -/** + /** * Emergency PDN connection for emergency calls * {@hide} */ @@ -746,7 +743,7 @@ public class ConnectivityManager { } /** - * Returns an array of of {@link NetworkCapabilities} objects, representing + * Returns an array of {@link android.net.NetworkCapabilities} objects, representing * the Networks that applications run by the given user will use by default. * @hide */ @@ -836,11 +833,11 @@ public class ConnectivityManager { } /** - * Get the {@link NetworkCapabilities} for the given {@link Network}. This + * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This * will return {@code null} if the network is unknown. * * @param network The {@link Network} object identifying the network in question. - * @return The {@link NetworkCapabilities} for the network, or {@code null}. + * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}. */ public NetworkCapabilities getNetworkCapabilities(Network network) { try { @@ -864,6 +861,7 @@ public class ConnectivityManager { * always indicates failure. * * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. + * @removed */ public int startUsingNetworkFeature(int networkType, String feature) { NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature); @@ -911,6 +909,7 @@ public class ConnectivityManager { * always indicates failure. * * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. + * @removed */ public int stopUsingNetworkFeature(int networkType, String feature) { NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature); @@ -1189,6 +1188,7 @@ public class ConnectivityManager { * * @deprecated Deprecated in favor of the {@link #requestNetwork}, * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api. + * @removed */ public boolean requestRouteToHost(int networkType, int hostAddress) { return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress)); @@ -1207,6 +1207,7 @@ public class ConnectivityManager { * @hide * @deprecated Deprecated in favor of the {@link #requestNetwork} and * {@link #bindProcessToNetwork} api. + * @removed */ public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) { try { @@ -2067,7 +2068,7 @@ public class ConnectivityManager { * changes capabilities but still satisfies the stated need. * * @param network The {@link Network} whose capabilities have changed. - * @param networkCapabilities The new {@link NetworkCapabilities} for this network. + * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network. */ public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {} @@ -2309,7 +2310,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link NetworkCapabilities}. + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. * * This {@link NetworkRequest} will live until released via * {@link #unregisterNetworkCallback} or the calling application exits. @@ -2328,7 +2329,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link NetworkCapabilities}, limited + * 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 @@ -2375,7 +2376,7 @@ public class ConnectivityManager { /** - * Request a network to satisfy a set of {@link NetworkCapabilities}. + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. * * This function behaves identically to the version that takes a NetworkCallback, but instead * of {@link NetworkCallback} a {@link PendingIntent} is used. This means @@ -2451,17 +2452,21 @@ public class ConnectivityManager { } /** - * Request connectivityservice to refresh network capabilities for the given - * {@link network}. This method returns true if the network is still active, false - * otherwise. Notice the method call assumes the caller has registered for - * listening NetworkCapabilities updates. + * Requests bandwidth update for a given {@link Network} and returns whether the update request + * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying + * network connection for updated bandwidth information. The caller will be notified via + * {@link ConnectivityManager.NetworkCallback} if there is an update. Notice that this + * method assumes that the caller has previously called {@link #registerNetworkCallback} to + * listen for network changes. * * @param network{@link Network} specifying which network you're interested. + * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid. + * * @hide */ - public boolean requestBwUpdate(Network network) { + public boolean requestBandwidthUpdate(Network network) { try { - return mService.requestBwUpdate(network); + return mService.requestBandwidthUpdate(network); } catch (RemoteException e) { return false; } @@ -2573,7 +2578,12 @@ public class ConnectivityManager { if (NetworkUtils.bindProcessToNetwork(netId)) { // Set HTTP proxy system properties to match network. // TODO: Deprecate this static method and replace it with a non-static version. - Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy()); + try { + Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy()); + } catch (SecurityException e) { + // The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy. + Log.e(TAG, "Can't set proxy properties", e); + } // Must flush DNS cache as new network may have different DNS resolutions. InetAddress.clearDnsCache(); // Must flush socket pool as idle sockets will be bound to previous network and may diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d6c0693..efc76b3 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -134,7 +134,7 @@ interface IConnectivityManager void registerNetworkFactory(in Messenger messenger, in String name); - boolean requestBwUpdate(in Network network); + boolean requestBandwidthUpdate(in Network network); void unregisterNetworkFactory(in Messenger messenger); diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index f10e530..3d065e3 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -17,6 +17,7 @@ package android.nfc.cardemulation; import android.content.ComponentName; +import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -28,6 +29,7 @@ import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; +import android.os.ResultReceiver; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; @@ -88,12 +90,24 @@ public final class ApduServiceInfo implements Parcelable { * The uid of the package the service belongs to */ final int mUid; + + /** + * Whether this service has dynamic resources + */ + final boolean mHasDynamicResources; + + /** + * Settings Activity for this service + */ + final String mSettingsActivityName; + /** * @hide */ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, - boolean requiresUnlock, int bannerResource, int uid) { + boolean requiresUnlock, int bannerResource, int uid, boolean hasDynamicResources, + String settingsActivityName) { this.mService = info; this.mDescription = description; this.mStaticAidGroups = new HashMap<String, AidGroup>(); @@ -108,6 +122,8 @@ public final class ApduServiceInfo implements Parcelable { } this.mBannerResourceId = bannerResource; this.mUid = uid; + this.mHasDynamicResources = hasDynamicResources; + this.mSettingsActivityName = settingsActivityName; } public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws @@ -156,6 +172,10 @@ public final class ApduServiceInfo implements Parcelable { false); mBannerResourceId = sa.getResourceId( com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1); + mHasDynamicResources = sa.getBoolean( + com.android.internal.R.styleable.HostApduService_dynamicResources, false); + mSettingsActivityName = sa.getString( + com.android.internal.R.styleable.HostApduService_settingsActivity); sa.recycle(); } else { TypedArray sa = res.obtainAttributes(attrs, @@ -166,6 +186,10 @@ public final class ApduServiceInfo implements Parcelable { mRequiresDeviceUnlock = false; mBannerResourceId = sa.getResourceId( com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1); + mHasDynamicResources = sa.getBoolean( + com.android.internal.R.styleable.OffHostApduService_dynamicResources, false); + mSettingsActivityName = sa.getString( + com.android.internal.R.styleable.HostApduService_settingsActivity); sa.recycle(); } @@ -359,6 +383,15 @@ public final class ApduServiceInfo implements Parcelable { return mService.loadLabel(pm); } + public CharSequence loadAppLabel(PackageManager pm) { + try { + return pm.getApplicationLabel(pm.getApplicationInfo( + mService.resolvePackageName, PackageManager.GET_META_DATA)); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + public Drawable loadIcon(PackageManager pm) { return mService.loadIcon(pm); } @@ -377,6 +410,11 @@ public final class ApduServiceInfo implements Parcelable { return null; } } + public boolean hasDynamicResources() { + return mHasDynamicResources; + } + + public String getSettingsActivityName() { return mSettingsActivityName; } @Override public String toString() { @@ -430,6 +468,8 @@ public final class ApduServiceInfo implements Parcelable { dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); dest.writeInt(mBannerResourceId); dest.writeInt(mUid); + dest.writeInt(mHasDynamicResources ? 1 : 0); + dest.writeString(mSettingsActivityName); }; public static final Parcelable.Creator<ApduServiceInfo> CREATOR = @@ -452,8 +492,11 @@ public final class ApduServiceInfo implements Parcelable { boolean requiresUnlock = source.readInt() != 0; int bannerResource = source.readInt(); int uid = source.readInt(); + boolean dynamicResources = source.readInt() != 0; + String settingsActivityName = source.readString(); return new ApduServiceInfo(info, onHost, description, staticAidGroups, - dynamicAidGroups, requiresUnlock, bannerResource, uid); + dynamicAidGroups, requiresUnlock, bannerResource, uid, dynamicResources, + settingsActivityName); } @Override @@ -479,5 +522,6 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" AID: " + aid); } } + pw.println(" Settings Activity: " + mSettingsActivityName); } } diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 64c2bc2..b94d4a6 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -90,6 +90,37 @@ public final class CardEmulation { public static final String CATEGORY_OTHER = "other"; /** + * Ordered broadcast that can be sent to your app to + * request a description and banner to be shown in + * Android Settings UI. + * When sent to you, this broadcast will contain the + * {@link #EXTRA_SERVICE_COMPONENT} extra to identify + * the service. + * + * Note that this broadcast will only be sent to your + * app, if a card emulation service in your app has requested + * its resources to be loaded dynamically. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_REQUEST_SERVICE_RESOURCES = + "android.nfc.cardemulation.action.REQUEST_SERVICE_RESOURCES"; + + /** + * The description of the service. Note that this must + * be localized by your app, as the String will be shown + * as is. + */ + public static final String EXTRA_DESCRIPTION = + "android.nfc.cardemulation.extra.DESCRIPTION"; + + /** + * The resource ID of the service banner to be shown + * for this service. + */ + public static final String EXTRA_BANNER_RES_ID = + "android.nfc.cardemulation.extra.BANNER_RES_ID"; + + /** * Return value for {@link #getSelectionModeForCategory(String)}. * * <p>In this mode, the user has set a default service for this diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 9d8a1ba..43309c0 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -34,6 +34,7 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.Serializable; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -283,6 +284,8 @@ public final class Parcel { private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName); private static native void nativeEnforceInterface(long nativePtr, String interfaceName); + private static native long nativeGetBlobAshmemSize(long nativePtr); + public final static Parcelable.Creator<String> STRING_CREATOR = new Parcelable.Creator<String>() { public String createFromParcel(Parcel source) { @@ -1373,8 +1376,7 @@ public final class Parcel { writeString(null); return; } - String name = p.getClass().getName(); - writeString(name); + writeParcelableCreator(p); p.writeToParcel(this, parcelableFlags); } @@ -2275,78 +2277,94 @@ public final class Parcel { * @throws BadParcelableException Throws BadParcelableException if there * was an error trying to instantiate the Parcelable. */ + @SuppressWarnings("unchecked") public final <T extends Parcelable> T readParcelable(ClassLoader loader) { - Parcelable.Creator<T> creator = readParcelableCreator(loader); + Parcelable.Creator<?> creator = readParcelableCreator(loader); if (creator == null) { return null; } if (creator instanceof Parcelable.ClassLoaderCreator<?>) { - return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader); + Parcelable.ClassLoaderCreator<?> classLoaderCreator = + (Parcelable.ClassLoaderCreator<?>) creator; + return (T) classLoaderCreator.createFromParcel(this, loader); } - return creator.createFromParcel(this); + return (T) creator.createFromParcel(this); } /** @hide */ - public final <T extends Parcelable> T readCreator(Parcelable.Creator<T> creator, + @SuppressWarnings("unchecked") + public final <T extends Parcelable> T readCreator(Parcelable.Creator<?> creator, ClassLoader loader) { if (creator instanceof Parcelable.ClassLoaderCreator<?>) { - return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader); + Parcelable.ClassLoaderCreator<?> classLoaderCreator = + (Parcelable.ClassLoaderCreator<?>) creator; + return (T) classLoaderCreator.createFromParcel(this, loader); } - return creator.createFromParcel(this); + return (T) creator.createFromParcel(this); } /** @hide */ - public final <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator( - ClassLoader loader) { + public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) { String name = readString(); if (name == null) { return null; } - Parcelable.Creator<T> creator; + Parcelable.Creator<?> creator; synchronized (mCreators) { - HashMap<String,Parcelable.Creator> map = mCreators.get(loader); + HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader); if (map == null) { - map = new HashMap<String,Parcelable.Creator>(); + map = new HashMap<>(); mCreators.put(loader, map); } creator = map.get(name); if (creator == null) { try { - Class c = loader == null ? - Class.forName(name) : Class.forName(name, true, loader); - Field f = c.getField("CREATOR"); - creator = (Parcelable.Creator)f.get(null); + // If loader == null, explicitly emulate Class.forName(String) "caller + // classloader" behavior. + ClassLoader parcelableClassLoader = + (loader == null ? getClass().getClassLoader() : loader); + // Avoid initializing the Parcelable class until we know it implements + // Parcelable and has the necessary CREATOR field. http://b/1171613. + Class<?> parcelableClass = Class.forName(name, false /* initialize */, + parcelableClassLoader); + if (!Parcelable.class.isAssignableFrom(parcelableClass)) { + throw new BadParcelableException("Parcelable protocol requires that the " + + "class implements Parcelable"); + } + Field f = parcelableClass.getField("CREATOR"); + if ((f.getModifiers() & Modifier.STATIC) == 0) { + throw new BadParcelableException("Parcelable protocol requires " + + "the CREATOR object to be static on class " + name); + } + Class<?> creatorType = f.getType(); + if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) { + // Fail before calling Field.get(), not after, to avoid initializing + // parcelableClass unnecessarily. + throw new BadParcelableException("Parcelable protocol requires a " + + "Parcelable.Creator object called " + + "CREATOR on class " + name); + } + creator = (Parcelable.Creator<?>) f.get(null); } catch (IllegalAccessException e) { - Log.e(TAG, "Illegal access when unmarshalling: " - + name, e); + Log.e(TAG, "Illegal access when unmarshalling: " + name, e); throw new BadParcelableException( "IllegalAccessException when unmarshalling: " + name); } catch (ClassNotFoundException e) { - Log.e(TAG, "Class not found when unmarshalling: " - + name, e); + Log.e(TAG, "Class not found when unmarshalling: " + name, e); throw new BadParcelableException( "ClassNotFoundException when unmarshalling: " + name); } - catch (ClassCastException e) { - throw new BadParcelableException("Parcelable protocol requires a " - + "Parcelable.Creator object called " - + " CREATOR on class " + name); - } catch (NoSuchFieldException e) { throw new BadParcelableException("Parcelable protocol requires a " - + "Parcelable.Creator object called " - + " CREATOR on class " + name); - } - catch (NullPointerException e) { - throw new BadParcelableException("Parcelable protocol requires " - + "the CREATOR object to be static on class " + name); + + "Parcelable.Creator object called " + + "CREATOR on class " + name); } if (creator == null) { throw new BadParcelableException("Parcelable protocol requires a " - + "Parcelable.Creator object called " - + " CREATOR on class " + name); + + "non-null Parcelable.Creator object called " + + "CREATOR on class " + name); } map.put(name, creator); @@ -2369,7 +2387,7 @@ public final class Parcel { } Parcelable[] p = new Parcelable[N]; for (int i = 0; i < N; i++) { - p[i] = (Parcelable) readParcelable(loader); + p[i] = readParcelable(loader); } return p; } @@ -2424,8 +2442,8 @@ public final class Parcel { // Cache of previously looked up CREATOR.createFromParcel() methods for // particular classes. Keys are the names of the classes, values are // Method objects. - private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator>> - mCreators = new HashMap<ClassLoader,HashMap<String,Parcelable.Creator>>(); + private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator<?>>> + mCreators = new HashMap<>(); /** @hide for internal use only. */ static protected final Parcel obtain(int obj) { @@ -2594,4 +2612,11 @@ public final class Parcel { N--; } } + + /** + * @hide For testing + */ + public long getBlobAshmemSize() { + return nativeGetBlobAshmemSize(mNativePtr); + } } diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java index 594fbb2..448b591 100644 --- a/core/java/android/os/Parcelable.java +++ b/core/java/android/os/Parcelable.java @@ -19,9 +19,8 @@ package android.os; /** * Interface for classes whose instances can be written to * and restored from a {@link Parcel}. Classes implementing the Parcelable - * interface must also have a static field called <code>CREATOR</code>, which - * is an object implementing the {@link Parcelable.Creator Parcelable.Creator} - * interface. + * interface must also have a non-null static field called <code>CREATOR</code> + * of a type that implements the {@link Parcelable.Creator} interface. * * <p>A typical implementation of Parcelable is:</p> * diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 9f38de8..64f2a05 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -68,6 +68,9 @@ public class DiskInfo implements Parcelable { if (TextUtils.isEmpty(label)) { return false; } + if (label.equalsIgnoreCase("ata")) { + return false; + } if (label.toLowerCase().contains("generic")) { return false; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index efa3ef2..f101352 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -69,6 +69,8 @@ public class StorageManager { /** {@hide} */ public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; + /** {@hide} */ + public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; /** {@hide} */ public static final int FLAG_ALL_METADATA = 1 << 0; diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index bf7f3cb..396cf19 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -2201,7 +2201,6 @@ public final class ContactsContract { public static final long DAYS_KEPT_MILLISECONDS = 1000L * 60L * 60L * 24L * (long)DAYS_KEPT; } - protected interface RawContactsColumns { /** * A reference to the {@link ContactsContract.Contacts#_ID} that this @@ -8921,4 +8920,147 @@ public final class ContactsContract { public static final String EXTRA_DATA_SET = "android.provider.extra.DATA_SET"; } } + + /** + * @hide + */ + protected interface MetadataSyncColumns { + + /** + * The raw contact backup id. + * A reference to the {@link ContactsContract.RawContacts#BACKUP_ID} that save the + * persistent unique id for each raw contact within its source system. + * + * @hide + */ + public static final String RAW_CONTACT_BACKUP_ID = "raw_contact_backup_id"; + + /** + * The account type to which the raw_contact of this item is associated. See + * {@link RawContacts#ACCOUNT_TYPE} + * + * @hide + */ + public static final String ACCOUNT_TYPE = "account_type"; + + /** + * The account name to which the raw_contact of this item is associated. See + * {@link RawContacts#ACCOUNT_NAME} + * + * @hide + */ + public static final String ACCOUNT_NAME = "account_name"; + + /** + * The data set within the account that the raw_contact of this row belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' data. + * {@link RawContacts#DATA_SET} + * + * @hide + */ + public static final String DATA_SET = "data_set"; + + /** + * A text column contains the Json string got from People API. The Json string contains + * all the metadata related to the raw contact, i.e., all the data fields and + * aggregation exceptions. + * + * Here is an example of the Json string got from the actual schema. + * <pre> + * { + * "unique_contact_id": { + * "account_type": "CUSTOM_ACCOUNT", + * "custom_account_type": "facebook", + * "account_name": "android-test", + * "contact_id": "1111111", + * "data_set": "FOCUS" + * }, + * "contact_prefs": { + * "send_to_voicemail": true, + * "starred": false, + * "pinned": 2 + * }, + * "aggregation_data": [ + * { + * "type": "TOGETHER", + * "contact_ids": [ + * { + * "account_type": "GOOGLE_ACCOUNT", + * "account_name": "android-test2", + * "contact_id": "2222222", + * "data_set": "GOOGLE_PLUS" + * }, + * { + * "account_type": "GOOGLE_ACCOUNT", + * "account_name": "android-test3", + * "contact_id": "3333333", + * "data_set": "CUSTOM", + * "custom_data_set": "custom type" + * } + * ] + * } + * ], + * "field_data": [ + * { + * "field_data_id": "1001", + * "field_data_prefs": { + * "is_primary": true, + * "is_super_primary": true + * }, + * "usage_stats": [ + * { + * "usage_type": "CALL", + * "last_time_used": 10000001, + * "usage_count": 10 + * } + * ] + * } + * ] + * } + * </pre> + * + * @hide + */ + public static final String DATA = "data"; + + /** + * The "deleted" flag: "0" by default, "1" if the row has been marked + * for deletion. When {@link android.content.ContentResolver#delete} is + * called on a raw contact, updating MetadataSync table to set the flag of the raw contact + * as "1", then metadata sync adapter deletes the raw contact metadata on the server. + * <P>Type: INTEGER</P> + * + * @hide + */ + public static final String DELETED = "deleted"; + } + + /** + * Constants for the metadata sync table. This table is used to cache the metadata_sync data + * from server before it is merged into other CP2 tables. + * + * @hide + */ + public static final class MetadataSync implements BaseColumns, MetadataSyncColumns { + + /** The authority for the contacts metadata */ + public static final String METADATA_AUTHORITY = "com.android.contacts.metadata"; + + /** A content:// style uri to the authority for the contacts metadata */ + public static final Uri METADATA_AUTHORITY_URI = Uri.parse( + "content://" + METADATA_AUTHORITY); + + /** + * This utility class cannot be instantiated + */ + private MetadataSync() { + } + + /** + * The content:// style URI for this table. + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(METADATA_AUTHORITY_URI, + "metadata_sync"); + } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a622a21..00c851b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -189,6 +189,36 @@ public final class Settings { "android.settings.USAGE_ACCESS_SETTINGS"; /** + * Activity Category: Show application settings related to usage access. + * <p> + * An activity that provides a user interface for adjusting usage access related + * preferences for its containing application. Optional but recommended for apps that + * use {@link android.Manifest.permission#PACKAGE_USAGE_STATS}. + * <p> + * The activity may define meta-data to describe what usage access is + * used for within their app with {@link #METADATA_USAGE_ACCESS_REASON}, which + * will be displayed in Settings. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = + "android.intent.category.USAGE_ACCESS_CONFIG"; + + /** + * Metadata key: Reason for needing usage access. + * <p> + * A key for metadata attached to an activity that receives action + * {@link #INTENT_CATEGORY_USAGE_ACCESS_CONFIG}, shown to the + * user as description of how the app uses usage access. + * <p> + */ + public static final String METADATA_USAGE_ACCESS_REASON = + "android.settings.metadata.USAGE_ACCESS_REASON"; + + /** * Activity Action: Show settings to allow configuration of security and * location privacy. * <p> @@ -5361,6 +5391,12 @@ public final class Settings { public static final String SMS_DEFAULT_APPLICATION = "sms_default_application"; /** + * Specifies the package name currently configured to be the default dialer application + * @hide + */ + public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application"; + + /** * Specifies the package name currently configured to be the emergency assistance application * * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java index 70cd388..7e87717 100644 --- a/core/java/android/security/NetworkSecurityPolicy.java +++ b/core/java/android/security/NetworkSecurityPolicy.java @@ -46,9 +46,9 @@ public class NetworkSecurityPolicy { * without TLS or STARTTLS) is permitted for this process. * * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and - * FTP stacks, {@link android.webkit.WebView}, {@link android.app.DownloadManager}, - * {@link android.media.MediaPlayer}) will refuse this process's requests to use cleartext - * traffic. Third-party libraries are strongly encouraged to honor this setting as well. + * FTP stacks, {@link android.app.DownloadManager}, {@link android.media.MediaPlayer}) will + * refuse this process's requests to use cleartext traffic. Third-party libraries are strongly + * encouraged to honor this setting as well. * * <p>This flag is honored on a best effort basis because it's impossible to prevent all * cleartext traffic from Android applications given the level of access provided to them. For @@ -56,6 +56,8 @@ public class NetworkSecurityPolicy { * because it cannot determine whether its traffic is in cleartext. However, most network * traffic from applications is handled by higher-level network stacks/components which can * honor this aspect of the policy. + * + * <p>NOTE: {@link android.webkit.WebView} does not honor this flag. */ public boolean isCleartextTrafficPermitted() { return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted(); diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index ec8f802..eedbc70 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -88,10 +88,10 @@ public class DisplayListCanvas extends Canvas { /////////////////////////////////////////////////////////////////////////// private DisplayListCanvas() { - super(nCreateDisplayListRenderer()); + super(nCreateDisplayListCanvas()); } - private static native long nCreateDisplayListRenderer(); + private static native long nCreateDisplayListCanvas(); public static void setProperty(String name, String value) { nSetProperty(name, value); diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 779560c..1ac3f45 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -747,8 +747,22 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final int KEYCODE_TV_TIMER_PROGRAMMING = 258; /** Key code constant: Help key. */ public static final int KEYCODE_HELP = 259; - - private static final int LAST_KEYCODE = KEYCODE_HELP; + /** Key code constant: Navigate to previous key. + * Goes backward by one item in an ordered collection of items. */ + public static final int KEYCODE_NAVIGATE_PREVIOUS = 260; + /** Key code constant: Navigate to next key. + * Advances to the next item in an ordered collection of items. */ + public static final int KEYCODE_NAVIGATE_NEXT = 261; + /** Key code constant: Navigate in key. + * Activates the item that currently has focus or expands to the next level of a navigation + * hierarchy. */ + public static final int KEYCODE_NAVIGATE_IN = 262; + /** Key code constant: Navigate out key. + * Backs out one level of a navigation hierarchy or collapses the item that currently has + * focus. */ + public static final int KEYCODE_NAVIGATE_OUT = 263; + + private static final int LAST_KEYCODE = KEYCODE_NAVIGATE_OUT; // NOTE: If you add a new keycode here you must also add it to: // isSystem() diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index 5cf2c5c..b055efe 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -130,7 +130,7 @@ public class ScaleGestureDetector { private float mFocusY; private boolean mQuickScaleEnabled; - private boolean mButtonScaleEnabled; + private boolean mStylusScaleEnabled; private float mCurrSpan; private float mPrevSpan; @@ -162,7 +162,7 @@ public class ScaleGestureDetector { private static final float SCALE_FACTOR = .5f; private static final int ANCHORED_SCALE_MODE_NONE = 0; private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1; - private static final int ANCHORED_SCALE_MODE_BUTTON = 2; + private static final int ANCHORED_SCALE_MODE_STYLUS = 2; /** @@ -212,9 +212,14 @@ public class ScaleGestureDetector { mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan); mHandler = handler; // Quick scale is enabled by default after JB_MR2 - if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) { + final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; + if (targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) { setQuickScaleEnabled(true); } + // Stylus scale is enabled by default after LOLLIPOP_MR1 + if (targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { + setStylusScaleEnabled(true); + } } /** @@ -315,14 +320,11 @@ public class ScaleGestureDetector { } final int count = event.getPointerCount(); - final int toolType = event.getToolType(0); - final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS - || toolType == MotionEvent.TOOL_TYPE_MOUSE; - final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1) + final boolean isStylusButtonDown = (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) && (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0; final boolean anchoredScaleCancelled = - mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown; + mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown; final boolean streamComplete = action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled; @@ -347,12 +349,12 @@ public class ScaleGestureDetector { } } - if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode() - && !streamComplete && isAnchoredScaleButtonDown) { + if (!mInProgress && mStylusScaleEnabled && !inAnchoredScaleMode() + && !streamComplete && isStylusButtonDown) { // Start of a button scale gesture mAnchoredScaleStartX = event.getX(); mAnchoredScaleStartY = event.getY(); - mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON; + mAnchoredScaleMode = ANCHORED_SCALE_MODE_STYLUS; mInitialSpan = 0; } @@ -503,24 +505,22 @@ public class ScaleGestureDetector { } /** - * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks - * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus - * first button) and drags the pointer on the screen. Note that this is enabled by default if - * the app targets API 23 and newer. + * Sets whether the associates {@link OnScaleGestureListener} should receive + * onScale callbacks when the user uses a stylus and presses the button. + * Note that this is enabled by default if the app targets API 23 and newer. * - * @param scales true to enable stylus or mouse scaling, false to disable. + * @param scales true to enable stylus scaling, false to disable. */ - public void setSecondaryButtonScaleEnabled(boolean scales) { - mButtonScaleEnabled = scales; + public void setStylusScaleEnabled(boolean scales) { + mStylusScaleEnabled = scales; } /** - * Return whether the button scale gesture, in which the user presses a - * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the - * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}. + * Return whether the stylus scale gesture, in which the user uses a stylus + * and presses the button, should preform scaling. {@see #setButtonScaleEnabled(boolean)}. */ - public boolean isSecondaryButtonScaleEnabled() { - return mButtonScaleEnabled; + public boolean isStylusScaleEnabled() { + return mStylusScaleEnabled; } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6176b9a..5c6ce76 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -631,6 +631,7 @@ import java.util.concurrent.atomic.AtomicInteger; * </p> * * @attr ref android.R.styleable#View_alpha + * @attr ref android.R.styleable#View_assistBlocked * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable * @attr ref android.R.styleable#View_contentDescription @@ -2324,6 +2325,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_IS_LAID_OUT * 1 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT * 1 PFLAG3_CALLED_SUPER + * 1 PFLAG3_APPLYING_INSETS + * 1 PFLAG3_FITTING_SYSTEM_WINDOWS + * 1 PFLAG3_NESTED_SCROLLING_ENABLED + * 1 PFLAG3_ASSIST_BLOCKED * |-------|-------|-------|-------| */ @@ -2381,6 +2386,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED; /** + * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse + * into this view.<p> + */ + static final int PFLAG3_ASSIST_BLOCKED = 0x100; + + /** * Always allow a user to over-scroll this view, provided it is a * view that can scroll. * @@ -3869,6 +3880,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, viewFlagMasks |= SAVE_DISABLED_MASK; } break; + case com.android.internal.R.styleable.View_assistBlocked: + if (a.getBoolean(attr, false)) { + mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED; + } + break; case com.android.internal.R.styleable.View_duplicateParentState: if (a.getBoolean(attr, false)) { viewFlagValues |= DUPLICATE_PARENT_STATE; @@ -5775,7 +5791,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { structure.setId(id, null, null, null); } - structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight-mLeft, mBottom-mTop); + structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop); structure.setVisibility(getVisibility()); structure.setEnabled(isEnabled()); if (isClickable()) { @@ -5890,8 +5906,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #onProvideVirtualAssistStructure}. */ public void dispatchProvideAssistStructure(ViewAssistStructure structure) { - onProvideAssistStructure(structure); - onProvideVirtualAssistStructure(structure); + if (!isAssistBlocked()) { + onProvideAssistStructure(structure); + onProvideVirtualAssistStructure(structure); + } else { + structure.setClassName(getAccessibilityClassName().toString()); + structure.setAssistBlocked(true); + } } /** @@ -7458,6 +7479,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Indicates whether this view will participate in data collection through + * {@link android.view.ViewAssistStructure}. If true, it will not provide any data + * for itself or its children. If false, the normal data collection will be allowed. + * + * @return Returns false if assist data collection is not blocked, else true. + * + * @see #setAssistBlocked(boolean) + * @attr ref android.R.styleable#View_assistBlocked + */ + public boolean isAssistBlocked() { + return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0; + } + + /** + * Controls whether assist data collection from this view and its children is enabled + * (that is, whether {@link #onProvideAssistStructure} and + * {@link #onProvideVirtualAssistStructure} will be called). The default value is false, + * allowing normal assist collection. Setting this to false will disable assist collection. + * + * @param enabled Set to true to <em>disable</em> assist data collection, or false + * (the default) to allow it. + * + * @see #isAssistBlocked() + * @see #onProvideAssistStructure + * @see #onProvideVirtualAssistStructure + * @attr ref android.R.styleable#View_assistBlocked + */ + public void setAssistBlocked(boolean enabled) { + if (enabled) { + mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED; + } else { + mPrivateFlags3 &= ~PFLAG3_ASSIST_BLOCKED; + } + } + + /** * Indicates whether this view will save its state (that is, * whether its {@link #onSaveInstanceState} method will be called). * @@ -15101,6 +15158,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (mClipBounds != null) ? new Rect(mClipBounds) : null; } + + /** + * Populates an output rectangle with the clip bounds of the view, + * returning {@code true} if successful or {@code false} if the view's + * clip bounds are {@code null}. + * + * @param outRect rectangle in which to place the clip bounds of the view + * @return {@code true} if successful or {@code false} if the view's + * clip bounds are {@code null} + */ + public boolean getClipBounds(Rect outRect) { + if (mClipBounds != null) { + outRect.set(mClipBounds); + return true; + } + return false; + } + /** * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common * case of an active Animation being run on the view. @@ -15283,9 +15358,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, RenderNode renderNode = null; Bitmap cache = null; - int layerType = getLayerType(); + int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local if (layerType == LAYER_TYPE_SOFTWARE || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) { + // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); cache = getDrawingCache(true); @@ -15312,10 +15388,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sy = mScrollY; } - final boolean hasNoCache = cache == null || drawingWithRenderNode; - final boolean offsetForScroll = cache == null - && !drawingWithRenderNode - && layerType != LAYER_TYPE_HARDWARE; + final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode; + final boolean offsetForScroll = cache == null && !drawingWithRenderNode; int restoreTo = -1; if (!drawingWithRenderNode || transformToApply != null) { @@ -15388,17 +15462,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA; } parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION; - if (hasNoCache) { + if (!drawingWithDrawingCache) { final int multipliedAlpha = (int) (255 * alpha); if (!onSetAlpha(multipliedAlpha)) { - int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; - if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 - || layerType != LAYER_TYPE_NONE) { - layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; - } if (drawingWithRenderNode) { renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha()); } else if (layerType == LAYER_TYPE_NONE) { + int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; + if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0) { + layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; + } canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(), multipliedAlpha, layerFlags); } @@ -15433,33 +15506,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - if (hasNoCache) { - boolean layerRendered = false; - if (layerType == LAYER_TYPE_HARDWARE && !drawingWithRenderNode) { - final HardwareLayer layer = getHardwareLayer(); - if (layer != null && layer.isValid()) { - int restoreAlpha = mLayerPaint.getAlpha(); - mLayerPaint.setAlpha((int) (alpha * 255)); - ((DisplayListCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint); - mLayerPaint.setAlpha(restoreAlpha); - layerRendered = true; - } else { - canvas.saveLayer(sx, sy, sx + getWidth(), sy + getHeight(), mLayerPaint); - } - } - - if (!layerRendered) { - if (!drawingWithRenderNode) { - // Fast path for layouts with no backgrounds - if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { - mPrivateFlags &= ~PFLAG_DIRTY_MASK; - dispatchDraw(canvas); - } else { - draw(canvas); - } - } else { + if (!drawingWithDrawingCache) { + if (drawingWithRenderNode) { + mPrivateFlags &= ~PFLAG_DIRTY_MASK; + ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags); + } else { + // Fast path for layouts with no backgrounds + if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; - ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags); + dispatchDraw(canvas); + } else { + draw(canvas); } } } else if (cache != null) { diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java index 7d263c5..346b8ec 100644 --- a/core/java/android/view/ViewAssistStructure.java +++ b/core/java/android/view/ViewAssistStructure.java @@ -32,6 +32,8 @@ public abstract class ViewAssistStructure { public abstract void setVisibility(int visibility); + public abstract void setAssistBlocked(boolean state); + public abstract void setEnabled(boolean state); public abstract void setClickable(boolean state); diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index a237afd..27304f5 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -1040,14 +1040,10 @@ public class ViewDebug { return methods; } - final ArrayList<Method> declaredMethods = new ArrayList(); - klass.getDeclaredMethodsUnchecked(false, declaredMethods); + methods = klass.getDeclaredMethodsUnchecked(false); final ArrayList<Method> foundMethods = new ArrayList<Method>(); - final int count = declaredMethods.size(); - for (int i = 0; i < count; i++) { - final Method method = declaredMethods.get(i); - + for (final Method method : methods) { // Ensure the method return and parameter types can be resolved. try { method.getReturnType(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 8f2be99..4324e75 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2881,21 +2881,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public void dispatchProvideAssistStructure(ViewAssistStructure structure) { super.dispatchProvideAssistStructure(structure); - if (structure.getChildCount() == 0) { - final int childrenCount = getChildCount(); - if (childrenCount > 0) { - structure.setChildCount(childrenCount); - final ArrayList<View> preorderedList = buildOrderedChildList(); - final boolean customOrder = preorderedList == null - && isChildrenDrawingOrderEnabled(); - final View[] children = mChildren; - for (int i=0; i<childrenCount; i++) { - final int childIndex = customOrder - ? getChildDrawingOrder(childrenCount, i) : i; - final View child = (preorderedList == null) - ? children[childIndex] : preorderedList.get(childIndex); - ViewAssistStructure cstructure = structure.newChild(i); - child.dispatchProvideAssistStructure(cstructure); + if (!isAssistBlocked()) { + if (structure.getChildCount() == 0) { + final int childrenCount = getChildCount(); + if (childrenCount > 0) { + structure.setChildCount(childrenCount); + final ArrayList<View> preorderedList = buildOrderedChildList(); + final boolean customOrder = preorderedList == null + && isChildrenDrawingOrderEnabled(); + final View[] children = mChildren; + for (int i=0; i<childrenCount; i++) { + final int childIndex = customOrder + ? getChildDrawingOrder(childrenCount, i) : i; + final View child = (preorderedList == null) + ? children[childIndex] : preorderedList.get(childIndex); + ViewAssistStructure cstructure = structure.newChild(i); + child.dispatchProvideAssistStructure(cstructure); + } } } } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 943beb0..453e4f5 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -900,7 +900,9 @@ public abstract class WebSettings { * and therefore secure policy, this setting should be disabled. * Note that this setting affects only JavaScript access to file scheme * resources. Other access to such resources, for example, from image HTML - * elements, is unaffected. + * elements, is unaffected. To prevent possible violation of same domain policy + * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier + * devices, you should explicitly set this value to {@code false}. * <p> * The default value is true for API level * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, @@ -920,7 +922,9 @@ public abstract class WebSettings { * the value of {@link #getAllowUniversalAccessFromFileURLs} is true. * Note too, that this setting affects only JavaScript access to file scheme * resources. Other access to such resources, for example, from image HTML - * elements, is unaffected. + * elements, is unaffected. To prevent possible violation of same domain policy + * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier + * devices, you should explicitly set this value to {@code false}. * <p> * The default value is true for API level * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 955ad06..b049e49 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -39,7 +39,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.inputmethodservice.ExtractEditText; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Parcel; @@ -104,7 +103,6 @@ import android.widget.TextView.OnEditorActionListener; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.EditableInputConnection; import java.text.BreakIterator; @@ -1764,15 +1762,55 @@ public class Editor { } /** - * @return <code>true</code> if the cursor/current selection overlaps a {@link SuggestionSpan}. + * @return <code>true</code> if it's reasonable to offer to show suggestions depending on + * the current cursor position or selection range. This method is consistent with the + * method to show suggestions {@link SuggestionsPopupWindow#updateSuggestions}. */ - private boolean isCursorInsideSuggestionSpan() { + private boolean shouldOfferToShowSuggestions() { CharSequence text = mTextView.getText(); if (!(text instanceof Spannable)) return false; - SuggestionSpan[] suggestionSpans = ((Spannable) text).getSpans( - mTextView.getSelectionStart(), mTextView.getSelectionEnd(), SuggestionSpan.class); - return (suggestionSpans.length > 0); + final Spannable spannable = (Spannable) text; + final int selectionStart = mTextView.getSelectionStart(); + final int selectionEnd = mTextView.getSelectionEnd(); + final SuggestionSpan[] suggestionSpans = spannable.getSpans(selectionStart, selectionEnd, + SuggestionSpan.class); + if (suggestionSpans.length == 0) { + return false; + } + if (selectionStart == selectionEnd) { + // Spans overlap the cursor. + return true; + } + int minSpanStart = mTextView.getText().length(); + int maxSpanEnd = 0; + int unionOfSpansCoveringSelectionStartStart = mTextView.getText().length(); + int unionOfSpansCoveringSelectionStartEnd = 0; + for (int i = 0; i < suggestionSpans.length; i++) { + final int spanStart = spannable.getSpanStart(suggestionSpans[i]); + final int spanEnd = spannable.getSpanEnd(suggestionSpans[i]); + minSpanStart = Math.min(minSpanStart, spanStart); + maxSpanEnd = Math.max(maxSpanEnd, spanEnd); + if (selectionStart < spanStart || selectionStart > spanEnd) { + // The span doesn't cover the current selection start point. + continue; + } + unionOfSpansCoveringSelectionStartStart = + Math.min(unionOfSpansCoveringSelectionStartStart, spanStart); + unionOfSpansCoveringSelectionStartEnd = + Math.max(unionOfSpansCoveringSelectionStartEnd, spanEnd); + } + if (unionOfSpansCoveringSelectionStartStart >= unionOfSpansCoveringSelectionStartEnd) { + // No spans cover the selection start point. + return false; + } + if (minSpanStart < unionOfSpansCoveringSelectionStartStart + || maxSpanEnd > unionOfSpansCoveringSelectionStartEnd) { + // There is a span that is not covered by the union. In this case, we soouldn't offer + // to show suggestions as it's confusing. + return false; + } + return true; } /** @@ -3020,18 +3058,8 @@ public class Editor { } private void populateMenuWithItems(Menu menu) { - final boolean legacy = mTextView.getContext().getApplicationInfo().targetSdkVersion < - Build.VERSION_CODES.LOLLIPOP; - final Context context = !legacy && menu instanceof MenuBuilder ? - ((MenuBuilder) menu).getContext() : - mTextView.getContext(); - final TypedArray styledAttributes = context.obtainStyledAttributes( - com.android.internal.R.styleable.SelectionModeDrawables); - if (mTextView.canCut()) { menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut). - setIcon(styledAttributes.getResourceId( - R.styleable.SelectionModeDrawables_actionModeCutDrawable, 0)). setAlphabeticShortcut('x'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); @@ -3039,8 +3067,6 @@ public class Editor { if (mTextView.canCopy()) { menu.add(0, TextView.ID_COPY, 0, com.android.internal.R.string.copy). - setIcon(styledAttributes.getResourceId( - R.styleable.SelectionModeDrawables_actionModeCopyDrawable, 0)). setAlphabeticShortcut('c'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); @@ -3048,23 +3074,23 @@ public class Editor { if (mTextView.canPaste()) { menu.add(0, TextView.ID_PASTE, 0, com.android.internal.R.string.paste). - setIcon(styledAttributes.getResourceId( - R.styleable.SelectionModeDrawables_actionModePasteDrawable, 0)). setAlphabeticShortcut('v'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); } + if (mTextView.canShare()) { + menu.add(0, TextView.ID_SHARE, 0, com.android.internal.R.string.share). + setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll). - setIcon(styledAttributes.getResourceId( - R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0)). setAlphabeticShortcut('a'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); updateReplaceItem(menu); - - styledAttributes.recycle(); } private void addIntentMenuItemsForTextProcessing(Menu menu) { @@ -3104,7 +3130,7 @@ public class Editor { } private void updateReplaceItem(Menu menu) { - boolean canReplace = mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan(); + boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions(); boolean replaceItemExists = menu.findItem(TextView.ID_REPLACE) != null; if (canReplace && !replaceItemExists) { menu.add(0, TextView.ID_REPLACE, 0, com.android.internal.R.string.replace). @@ -4025,9 +4051,9 @@ public class Editor { positionCursor = true; } else if (offset + mTouchWordOffset < mPreviousOffset) { // User is shrinking the selection. - if (currLine > mPrevLine) { + if (currLine < mPrevLine) { // We're on a different line, so we'll snap to word boundaries. - offset = getWordStart(offset); + offset = start; } offset += mTouchWordOffset; positionCursor = true; diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index c5b5c84..c3ac278 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -94,7 +94,6 @@ public class PopupWindow { private final int[] mDrawingLocation = new int[2]; private final int[] mScreenLocation = new int[2]; private final Rect mTempRect = new Rect(); - private final Rect mAnchorBounds = new Rect(); private Context mContext; private WindowManager mWindowManager; @@ -159,28 +158,6 @@ public class PopupWindow { private WeakReference<View> mAnchor; - private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() { - @Override - public Rect onGetEpicenter(Transition transition) { - final View anchor = mAnchor != null ? mAnchor.get() : null; - final View decor = mDecorView; - if (anchor == null || decor == null) { - return null; - } - - final Rect anchorBounds = mAnchorBounds; - final int[] anchorLocation = anchor.getLocationOnScreen(); - final int[] popupLocation = mDecorView.getLocationOnScreen(); - - // Compute the position of the anchor relative to the popup. - anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight()); - anchorBounds.offset(anchorLocation[0] - popupLocation[0], - anchorLocation[1] - popupLocation[1]); - - return anchorBounds; - } - }; - private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() { @Override public void onScrollChanged() { @@ -355,18 +332,10 @@ public class PopupWindow { public void setEnterTransition(Transition enterTransition) { mEnterTransition = enterTransition; - - if (mEnterTransition != null) { - mEnterTransition.setEpicenterCallback(mEpicenterCallback); - } } public void setExitTransition(Transition exitTransition) { mExitTransition = exitTransition; - - if (mExitTransition != null) { - mExitTransition.setEpicenterCallback(mEpicenterCallback); - } } private Transition getTransition(int resId) { @@ -1281,11 +1250,14 @@ public class PopupWindow { final PopupDecorView decorView = mDecorView; decorView.setFitsSystemWindows(mLayoutInsetDecor); - decorView.requestEnterTransition(mEnterTransition); setLayoutDirectionFromAnchor(); mWindowManager.addView(decorView, p); + + if (mEnterTransition != null) { + decorView.requestEnterTransition(mEnterTransition); + } } private void setLayoutDirectionFromAnchor() { @@ -1607,13 +1579,25 @@ public class PopupWindow { // Ensure any ongoing or pending transitions are canceled. decorView.cancelTransitions(); - unregisterForScrollChanged(); - mIsShowing = false; mIsTransitioningToDismiss = true; - if (mExitTransition != null && decorView.isLaidOut()) { - decorView.startExitTransition(mExitTransition, new TransitionListenerAdapter() { + final Transition exitTransition = mExitTransition; + if (exitTransition != null && decorView.isLaidOut()) { + // The decor view is non-interactive during exit transitions. + final LayoutParams p = (LayoutParams) decorView.getLayoutParams(); + p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; + p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; + mWindowManager.updateViewLayout(decorView, p); + + final Rect epicenter = getRelativeAnchorBounds(); + exitTransition.setEpicenterCallback(new EpicenterCallback() { + @Override + public Rect onGetEpicenter(Transition transition) { + return epicenter; + } + }); + decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { dismissImmediate(decorView, contentHolder, contentView); @@ -1623,11 +1607,30 @@ public class PopupWindow { dismissImmediate(decorView, contentHolder, contentView); } + // Clears the anchor view. + unregisterForScrollChanged(); + if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); } } + private Rect getRelativeAnchorBounds() { + final View anchor = mAnchor != null ? mAnchor.get() : null; + final View decor = mDecorView; + if (anchor == null || decor == null) { + return null; + } + + final int[] anchorLocation = anchor.getLocationOnScreen(); + final int[] popupLocation = mDecorView.getLocationOnScreen(); + + // Compute the position of the anchor relative to the popup. + final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight()); + bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]); + return bounds; + } + /** * Removes the popup from the window manager and tears down the supporting * view hierarchy, if necessary. @@ -1996,6 +1999,13 @@ public class PopupWindow { observer.removeOnGlobalLayoutListener(this); } + final Rect epicenter = getRelativeAnchorBounds(); + enterTransition.setEpicenterCallback(new EpicenterCallback() { + @Override + public Rect onGetEpicenter(Transition transition) { + return epicenter; + } + }); startEnterTransition(enterTransition); } }); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 726b89a..3e8df08 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8955,13 +8955,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener static final int ID_CUT = android.R.id.cut; static final int ID_COPY = android.R.id.copy; static final int ID_PASTE = android.R.id.paste; + static final int ID_SHARE = android.R.id.shareText; static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText; static final int ID_REPLACE = android.R.id.replaceText; /** * Called when a context menu option for the text view is selected. Currently * this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut}, - * {@link android.R.id#copy} or {@link android.R.id#paste}. + * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}. * * @return true if the context menu item action was performed. */ @@ -9014,6 +9015,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max))); stopSelectionActionMode(); return true; + + case ID_SHARE: + shareSelectedText(); + return true; } return false; } @@ -9091,15 +9096,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * If provided, this ActionMode.Callback will be used to create the ActionMode when text * selection is initiated in this View. * - * The standard implementation populates the menu with a subset of Select All, Cut, Copy and - * Paste actions, depending on what this View supports. + * The standard implementation populates the menu with a subset of Select All, Cut, Copy, + * Paste and Share actions, depending on what this View supports. * * A custom implementation can add new entries in the default menu in its * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The * default actions can also be removed from the menu using * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll}, - * {@link android.R.id#cut}, {@link android.R.id#copy} or {@link android.R.id#paste} ids as - * parameters. + * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste} or + * {@link android.R.id#shareText} ids as parameters. * * Returning false from * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent @@ -9168,6 +9173,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } + boolean canShare() { + return canCopy(); + } + boolean canPaste() { return (mText instanceof Editable && mEditor != null && mEditor.mKeyListener != null && @@ -9241,6 +9250,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + private void shareSelectedText() { + String selectedText = getSelectedText(); + if (selectedText != null && !selectedText.isEmpty()) { + Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); + sharingIntent.setType("text/plain"); + sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT); + sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText); + getContext().startActivity(Intent.createChooser(sharingIntent, null)); + stopSelectionActionMode(); + } + } + private void setPrimaryClip(ClipData clip) { ClipboardManager clipboard = (ClipboardManager) getContext(). getSystemService(Context.CLIPBOARD_SERVICE); diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags index b9208ff..4364cc3 100644 --- a/core/java/com/android/internal/logging/EventLogTags.logtags +++ b/core/java/com/android/internal/logging/EventLogTags.logtags @@ -4,6 +4,6 @@ option java_package com.android.internal.logging; # interaction logs 524287 sysui_view_visibility (category|1|5),(visible|1|6) -524288 sysui_action (category|1|5) +524288 sysui_action (category|1|5),(pkg|3) 524290 sysui_count (name|3),(increment|1) 524291 sysui_histogram (name|3),(bucket|1) diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 24b5d0d..092c148 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -30,6 +30,7 @@ public class MetricsLogger implements MetricsConstants { public static final int NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; public static final int NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145; + public static final int ACTION_BAN_APP_NOTES = 146; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { @@ -46,10 +47,14 @@ public class MetricsLogger implements MetricsConstants { } public static void action(Context context, int category) { + action(context, category, ""); + } + + public static void action(Context context, int category, String pkg) { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { throw new IllegalArgumentException("Must define metric category"); } - EventLogTags.writeSysuiAction(category); + EventLogTags.writeSysuiAction(category, pkg); } /** Add an integer value to the monotonically increasing counter with the given name. */ diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 3ad4f1c..5ed4f70 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -269,7 +269,12 @@ public class ZygoteInit { if (false) { Log.v(TAG, "Preloading " + line + "..."); } - Class.forName(line); + // Load and explicitly initialize the given class. Use + // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups + // (to derive the caller's class-loader). Use true to force initialization, and + // null for the boot classpath class-loader (could as well cache the + // class-loader of this class in a variable). + Class.forName(line, true, null); count++; } catch (ClassNotFoundException e) { Log.w(TAG, "Class not found for preloading: " + line); diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java index abb50c1..1a6736a 100644 --- a/core/java/com/android/internal/transition/EpicenterClipReveal.java +++ b/core/java/com/android/internal/transition/EpicenterClipReveal.java @@ -17,15 +17,23 @@ package com.android.internal.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.RectEvaluator; +import android.animation.TimeInterpolator; import android.content.Context; +import android.content.res.TypedArray; import android.graphics.Rect; import android.transition.TransitionValues; import android.transition.Visibility; import android.util.AttributeSet; +import android.util.Property; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.view.animation.PathInterpolator; + +import com.android.internal.R; /** * EpicenterClipReveal captures the {@link View#getClipBounds()} before and @@ -36,10 +44,39 @@ public class EpicenterClipReveal extends Visibility { private static final String PROPNAME_CLIP = "android:epicenterReveal:clip"; private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; - public EpicenterClipReveal() {} + private final TimeInterpolator mInterpolatorX; + private final TimeInterpolator mInterpolatorY; + private final boolean mCenterClipBounds; + + public EpicenterClipReveal() { + mInterpolatorX = null; + mInterpolatorY = null; + mCenterClipBounds = false; + } public EpicenterClipReveal(Context context, AttributeSet attrs) { super(context, attrs); + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.EpicenterClipReveal, 0, 0); + + mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false); + + final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0); + if (interpolatorX != 0) { + mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); + } else { + mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN; + } + + final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0); + if (interpolatorY != 0) { + mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); + } else { + mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; + } + + a.recycle(); } @Override @@ -82,7 +119,7 @@ public class EpicenterClipReveal extends Visibility { // Prepare the view. view.setClipBounds(start); - return createRectAnimator(view, start, end, endValues); + return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); } @Override @@ -98,17 +135,23 @@ public class EpicenterClipReveal extends Visibility { // Prepare the view. view.setClipBounds(start); - return createRectAnimator(view, start, end, endValues); + return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); } private Rect getEpicenterOrCenter(Rect bestRect) { final Rect epicenter = getEpicenter(); if (epicenter != null) { + // Translate the clip bounds to be centered within the target bounds. + if (mCenterClipBounds) { + final int offsetX = bestRect.centerX() - epicenter.centerX(); + final int offsetY = bestRect.centerY() - epicenter.centerY(); + epicenter.offset(offsetX, offsetY); + } return epicenter; } - int centerX = bestRect.centerX(); - int centerY = bestRect.centerY(); + final int centerX = bestRect.centerX(); + final int centerY = bestRect.centerY(); return new Rect(centerX, centerY, centerX, centerY); } @@ -120,17 +163,71 @@ public class EpicenterClipReveal extends Visibility { return clipRect; } - private Animator createRectAnimator(final View view, Rect start, Rect end, - TransitionValues endValues) { - final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); + private static Animator createRectAnimator(final View view, Rect start, Rect end, + TransitionValues endValues, TimeInterpolator interpolatorX, + TimeInterpolator interpolatorY) { final RectEvaluator evaluator = new RectEvaluator(new Rect()); - ObjectAnimator anim = ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end); - anim.addListener(new AnimatorListenerAdapter() { + final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); + + final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X); + final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end); + if (interpolatorX != null) { + animX.setInterpolator(interpolatorX); + } + + final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y); + final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end); + if (interpolatorY != null) { + animY.setInterpolator(interpolatorY); + } + + final AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(animX, animY); + animSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setClipBounds(terminalClip); } }); - return anim; + return animSet; + } + + private static class ClipDimenProperty extends Property<View, Rect> { + public static final char TARGET_X = 'x'; + public static final char TARGET_Y = 'y'; + + private final Rect mTempRect = new Rect(); + + private final int mTargetDimension; + + public ClipDimenProperty(char targetDimension) { + super(Rect.class, "clip_bounds_" + targetDimension); + + mTargetDimension = targetDimension; + } + + @Override + public Rect get(View object) { + final Rect tempRect = mTempRect; + if (!object.getClipBounds(tempRect)) { + tempRect.setEmpty(); + } + return tempRect; + } + + @Override + public void set(View object, Rect value) { + final Rect tempRect = mTempRect; + if (object.getClipBounds(tempRect)) { + if (mTargetDimension == TARGET_X) { + tempRect.left = value.left; + tempRect.right = value.right; + } else { + tempRect.top = value.top; + tempRect.bottom = value.bottom; + } + object.setClipBounds(tempRect); + } + } } } diff --git a/core/java/com/android/internal/transition/EpicenterTranslate.java b/core/java/com/android/internal/transition/EpicenterTranslate.java new file mode 100644 index 0000000..eac3ff8 --- /dev/null +++ b/core/java/com/android/internal/transition/EpicenterTranslate.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.transition; + +import com.android.internal.R; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.transition.TransitionValues; +import android.transition.Visibility; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; + +/** + * EpicenterTranslate captures the {@link View#getTranslationX()} and + * {@link View#getTranslationY()} before and after the scene change and + * animates between those and the epicenter's center during a visibility + * transition. + */ +public class EpicenterTranslate extends Visibility { + private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; + private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX"; + private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY"; + private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ"; + private static final String PROPNAME_Z = "android:epicenterReveal:z"; + + private final TimeInterpolator mInterpolatorX; + private final TimeInterpolator mInterpolatorY; + private final TimeInterpolator mInterpolatorZ; + + public EpicenterTranslate() { + mInterpolatorX = null; + mInterpolatorY = null; + mInterpolatorZ = null; + } + + public EpicenterTranslate(Context context, AttributeSet attrs) { + super(context, attrs); + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.EpicenterTranslate, 0, 0); + + final int interpolatorX = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorX, 0); + if (interpolatorX != 0) { + mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); + } else { + mInterpolatorX = TransitionConstants.FAST_OUT_SLOW_IN; + } + + final int interpolatorY = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorY, 0); + if (interpolatorY != 0) { + mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); + } else { + mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; + } + + final int interpolatorZ = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorZ, 0); + if (interpolatorZ != 0) { + mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ); + } else { + mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN; + } + + a.recycle(); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + captureValues(transitionValues); + } + + private void captureValues(TransitionValues values) { + final View view = values.view; + if (view.getVisibility() == View.GONE) { + return; + } + + final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight()); + values.values.put(PROPNAME_BOUNDS, bounds); + values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX()); + values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY()); + values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ()); + values.values.put(PROPNAME_Z, view.getZ()); + } + + @Override + public Animator onAppear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + if (endValues == null) { + return null; + } + + final Rect end = (Rect) endValues.values.get(PROPNAME_BOUNDS); + final Rect start = getEpicenterOrCenter(end); + final float startX = start.centerX() - end.centerX(); + final float startY = start.centerY() - end.centerY(); + final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z); + + // Translate the view to be centered on the epicenter. + view.setTranslationX(startX); + view.setTranslationY(startY); + view.setTranslationZ(startZ); + + final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X); + final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y); + final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z); + return createAnimator(view, startX, startY, startZ, endX, endY, endZ, + mInterpolatorX, mInterpolatorY, mInterpolatorZ); + } + + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + if (startValues == null) { + return null; + } + + final Rect start = (Rect) endValues.values.get(PROPNAME_BOUNDS); + final Rect end = getEpicenterOrCenter(start); + final float endX = end.centerX() - start.centerX(); + final float endY = end.centerY() - start.centerY(); + final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z); + + final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X); + final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y); + final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z); + return createAnimator(view, startX, startY, startZ, endX, endY, endZ, + mInterpolatorX, mInterpolatorY, mInterpolatorZ); + } + + private Rect getEpicenterOrCenter(Rect bestRect) { + final Rect epicenter = getEpicenter(); + if (epicenter != null) { + return epicenter; + } + + final int centerX = bestRect.centerX(); + final int centerY = bestRect.centerY(); + return new Rect(centerX, centerY, centerX, centerY); + } + + private static Animator createAnimator(final View view, float startX, float startY, + float startZ, float endX, float endY, float endZ, TimeInterpolator interpolatorX, + TimeInterpolator interpolatorY, TimeInterpolator interpolatorZ) { + final ObjectAnimator animX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX); + if (interpolatorX != null) { + animX.setInterpolator(interpolatorX); + } + + final ObjectAnimator animY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY); + if (interpolatorY != null) { + animY.setInterpolator(interpolatorY); + } + + final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ); + if (interpolatorZ != null) { + animZ.setInterpolator(interpolatorZ); + } + + final AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(animX, animY, animZ); + return animSet; + } +} diff --git a/core/java/com/android/internal/transition/TransitionConstants.java b/core/java/com/android/internal/transition/TransitionConstants.java new file mode 100644 index 0000000..e9015e6 --- /dev/null +++ b/core/java/com/android/internal/transition/TransitionConstants.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.transition; + +import android.animation.TimeInterpolator; +import android.view.animation.PathInterpolator; + +class TransitionConstants { + static final TimeInterpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0, 0, 0.2f, 1); + static final TimeInterpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0, 0.2f, 1); +} diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index a14e98d..3a1e0ca 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -346,6 +346,17 @@ public final class FloatingToolbar { }; private final Region mTouchableRegion = new Region(); + private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = + new ViewTreeObserver.OnComputeInternalInsetsListener() { + public void onComputeInternalInsets( + ViewTreeObserver.InternalInsetsInfo info) { + info.contentInsets.setEmpty(); + info.visibleInsets.setEmpty(); + info.touchableRegion.set(mTouchableRegion); + info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo + .TOUCHABLE_INSETS_REGION); + } + }; private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing. private boolean mHidden; // tracks whether this popup is hidden or hiding. @@ -382,21 +393,6 @@ public final class FloatingToolbar { mPopupWindow.dismiss(); } }); - // Make the touchable area of this popup be the area specified by mTouchableRegion. - mPopupWindow.getContentView() - .getRootView() - .getViewTreeObserver() - .addOnComputeInternalInsetsListener( - new ViewTreeObserver.OnComputeInternalInsetsListener() { - public void onComputeInternalInsets( - ViewTreeObserver.InternalInsetsInfo info) { - info.contentInsets.setEmpty(); - info.visibleInsets.setEmpty(); - info.touchableRegion.set(mTouchableRegion); - info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo - .TOUCHABLE_INSETS_REGION); - } - }); mMarginHorizontal = parent.getResources() .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin); mMarginVertical = parent.getResources() @@ -437,16 +433,15 @@ public final class FloatingToolbar { mHidden = false; mDismissed = false; - cancelAllAnimations(); + cancelDismissAndHideAnimations(); + cancelOverflowAnimations(); // Make sure a panel is set as the content. if (mContentContainer.getChildCount() == 0) { setMainPanelAsContent(); } preparePopupContent(); - // If we're yet to show the popup, set the container visibility to zero. - // The "show" animation will make this visible. - mContentContainer.setAlpha(0); mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y); + setTouchableSurfaceInsetsComputer(); runShowAnimation(); } @@ -454,12 +449,13 @@ public final class FloatingToolbar { * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op. */ public void dismiss() { - if (!isShowing()) { + if (mDismissed) { return; } mHidden = false; mDismissed = true; + mHideAnimation.cancel(); runDismissAnimation(); setZeroTouchableSurface(); } @@ -502,7 +498,7 @@ public final class FloatingToolbar { return; } - cancelAllAnimations(); + cancelOverflowAnimations(); preparePopupContent(); mPopupWindow.update(x, y, getWidth(), getHeight()); } @@ -566,10 +562,12 @@ public final class FloatingToolbar { mHideAnimation.start(); } - private void cancelAllAnimations() { - mShowAnimation.cancel(); + private void cancelDismissAndHideAnimations() { mDismissAnimation.cancel(); mHideAnimation.cancel(); + } + + private void cancelOverflowAnimations() { mOpenOverflowAnimation.cancel(); mCloseOverflowAnimation.cancel(); } @@ -804,6 +802,19 @@ public final class FloatingToolbar { (int) mContentContainer.getX() + width, (int) mContentContainer.getY() + height); } + + /** + * Make the touchable area of this popup be the area specified by mTouchableRegion. + * This should be called after the popup window has been dismissed (dismiss/hide) + * and is probably being re-shown with a new content root view. + */ + private void setTouchableSurfaceInsetsComputer() { + ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView() + .getRootView() + .getViewTreeObserver(); + viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer); + viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer); + } } /** diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index aa60eba..1096e34 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -24,7 +24,6 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.os.AsyncTask; import android.os.IBinder; import android.os.RemoteException; @@ -32,7 +31,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManager; import android.os.storage.IMountService; import android.os.storage.StorageManager; import android.provider.Settings; @@ -544,8 +542,7 @@ public class LockPatternUtils { // Update the device encryption password. if (userId == UserHandle.USER_OWNER && LockPatternUtils.isDeviceEncryptionEnabled()) { - final boolean required = isCredentialRequiredToDecrypt(true); - if (!required) { + if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { String stringPattern = patternToString(pattern); @@ -759,7 +756,7 @@ public class LockPatternUtils { // Update the device encryption password. if (userHandle == UserHandle.USER_OWNER && LockPatternUtils.isDeviceEncryptionEnabled()) { - if (!isCredentialRequiredToDecrypt(true)) { + if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { boolean numeric = computedQuality @@ -1238,4 +1235,12 @@ public class LockPatternUtils { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0); } + + private boolean isDoNotAskCredentialsOnBootSet() { + return mDevicePolicyManager.getDoNotAskCredentialsOnBoot(); + } + + private boolean shouldEncryptWithCredentials(boolean defaultValue) { + return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet(); + } } diff --git a/core/java/com/android/server/backup/NotificationBackupHelper.java b/core/java/com/android/server/backup/NotificationBackupHelper.java new file mode 100644 index 0000000..6f5c7e8 --- /dev/null +++ b/core/java/com/android/server/backup/NotificationBackupHelper.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup; + +import android.app.INotificationManager; +import android.app.backup.BlobBackupHelper; +import android.content.Context; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; +import android.util.Slog; + +public class NotificationBackupHelper extends BlobBackupHelper { + static final String TAG = "NotifBackupHelper"; // must be < 23 chars + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + // Current version of the blob schema + static final int BLOB_VERSION = 1; + + // Key under which the payload blob is stored + static final String KEY_NOTIFICATIONS = "notifications"; + + public NotificationBackupHelper(Context context) { + super(BLOB_VERSION, KEY_NOTIFICATIONS); + // context is currently unused + } + + @Override + protected byte[] getBackupPayload(String key) { + byte[] newPayload = null; + if (KEY_NOTIFICATIONS.equals(key)) { + try { + INotificationManager nm = INotificationManager.Stub.asInterface( + ServiceManager.getService("notification")); + newPayload = nm.getBackupPayload(UserHandle.USER_OWNER); + } catch (Exception e) { + // Treat as no data + Slog.e(TAG, "Couldn't communicate with notification manager"); + newPayload = null; + } + } + return newPayload; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + if (DEBUG) { + Slog.v(TAG, "Got restore of " + key); + } + + if (KEY_NOTIFICATIONS.equals(key)) { + try { + INotificationManager nm = INotificationManager.Stub.asInterface( + ServiceManager.getService("notification")); + nm.applyRestore(payload, UserHandle.USER_OWNER); + } catch (Exception e) { + Slog.e(TAG, "Couldn't communicate with notification manager"); + } + } + } + +} diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java index 6ac0d89..26f5bf4 100644 --- a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java +++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java @@ -17,159 +17,58 @@ package com.android.server.backup; import android.app.AppGlobals; -import android.app.backup.BackupDataInputStream; -import android.app.backup.BackupDataOutput; -import android.app.backup.BackupHelper; -import android.content.Context; +import android.app.backup.BlobBackupHelper; import android.content.pm.IPackageManager; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.ServiceManager; import android.os.UserHandle; import android.util.Slog; -import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; -import com.android.org.bouncycastle.util.Arrays; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; - -public class PreferredActivityBackupHelper implements BackupHelper { +public class PreferredActivityBackupHelper extends BlobBackupHelper { private static final String TAG = "PreferredBackup"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; // current schema of the backup state blob - private static final int STATE_VERSION = 1; + private static final int STATE_VERSION = 2; // key under which the preferred-activity state blob is committed to backup private static final String KEY_PREFERRED = "preferred-activity"; - final Context mContext; - - public PreferredActivityBackupHelper(Context context) { - mContext = context; - } - - // The fds passed here are shared among all helpers, so we mustn't close them - private void writeState(ParcelFileDescriptor stateFile, byte[] payload) { - try { - FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); - - // We explicitly don't close 'out' because we must not close the backing fd. - // The FileOutputStream will not close it implicitly. - @SuppressWarnings("resource") - DataOutputStream out = new DataOutputStream(fos); - - out.writeInt(STATE_VERSION); - if (payload == null) { - out.writeInt(0); - } else { - out.writeInt(payload.length); - out.write(payload); - } - } catch (IOException e) { - Slog.e(TAG, "Unable to write updated state", e); - } - } - - private byte[] readState(ParcelFileDescriptor oldStateFd) { - FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); - BufferedInputStream bis = new BufferedInputStream(fis); - - @SuppressWarnings("resource") - DataInputStream in = new DataInputStream(bis); - - byte[] oldState = null; - try { - int version = in.readInt(); - if (version == STATE_VERSION) { - int size = in.readInt(); - if (size > 0) { - if (size > 200*1024) { - Slog.w(TAG, "Suspiciously large state blog; ignoring. N=" + size); - } else { - // size looks okay; make the return buffer and fill it - oldState = new byte[size]; - in.read(oldState); - } - } - } else { - Slog.w(TAG, "Prior state from unrecognized version " + version); - } - } catch (EOFException e) { - // Empty file is expected on first backup, so carry on. If the state - // is truncated we just treat it the same way. - oldState = null; - } catch (Exception e) { - Slog.w(TAG, "Error examing prior backup state " + e.getMessage()); - oldState = null; - } - - return oldState; + public PreferredActivityBackupHelper() { + super(STATE_VERSION, KEY_PREFERRED); } @Override - public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { - byte[] payload = null; - try { - byte[] oldPayload = readState(oldState); - + protected byte[] getBackupPayload(String key) { + if (KEY_PREFERRED.equals(key)) { + if (DEBUG) { + Slog.v(TAG, "Checking whether to back up"); + } IPackageManager pm = AppGlobals.getPackageManager(); - byte[] newPayload = pm.getPreferredActivityBackup(UserHandle.USER_OWNER); - if (!Arrays.areEqual(oldPayload, newPayload)) { - if (DEBUG) { - Slog.i(TAG, "State has changed => writing new preferred app payload"); - } - data.writeEntityHeader(KEY_PREFERRED, newPayload.length); - data.writeEntityData(newPayload, newPayload.length); - } else { - if (DEBUG) { - Slog.i(TAG, "No change to state => not writing to wire"); - } + try { + return pm.getPreferredActivityBackup(UserHandle.USER_OWNER); + } catch (Exception e) { + Slog.e(TAG, "Unable to store backup payload", e); + // fall through to report null state } - - // Always need to re-record the state, even if nothing changed - payload = newPayload; - } catch (Exception e) { - // On failures we'll wind up committing a zero-size state payload. This is - // a forward-safe situation because we know we commit the entire new payload - // on prior-state mismatch. - Slog.w(TAG, "Unable to record preferred activities", e); - } finally { - writeState(newState, payload); + } else { + Slog.w(TAG, "Unexpected backup key " + key); } + return null; } @Override - public void restoreEntity(BackupDataInputStream data) { - IPackageManager pm = AppGlobals.getPackageManager(); - try { - byte[] payload = new byte[data.size()]; - data.read(payload); + protected void applyRestoredPayload(String key, byte[] payload) { + if (KEY_PREFERRED.equals(key)) { if (DEBUG) { - Slog.i(TAG, "Restoring preferred activities; size=" + payload.length); + Slog.v(TAG, "Restoring"); } - pm.restorePreferredActivities(payload, UserHandle.USER_OWNER); - } catch (Exception e) { - Slog.e(TAG, "Exception reading restore data", e); + IPackageManager pm = AppGlobals.getPackageManager(); + try { + pm.restorePreferredActivities(payload, UserHandle.USER_OWNER); + } catch (Exception e) { + Slog.e(TAG, "Unable to restore", e); + } + } else { + Slog.w(TAG, "Unexpected restore key " + key); } } - - @Override - public void writeNewStateDescription(ParcelFileDescriptor newState) { - writeState(newState, null); - } - } diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 19d9e29..a80abce 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -48,6 +48,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String RECENTS_HELPER = "recents"; private static final String SYNC_SETTINGS_HELPER = "account_sync_settings"; private static final String PREFERRED_HELPER = "preferred_activities"; + private static final String NOTIFICATION_HELPER = "notifications"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are @@ -93,7 +94,8 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, files, keys)); addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); - addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); + addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); + addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); super.onBackup(oldState, data, newState); } @@ -127,7 +129,8 @@ public class SystemBackupAgent extends BackupAgentHelper { new String[] { WALLPAPER_IMAGE_KEY} )); addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); - addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); + addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); + addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); try { super.onRestore(data, appVersionCode, newState); |
