diff options
Diffstat (limited to 'core/java/android')
54 files changed, 1514 insertions, 324 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 6cf6481..4ccde1c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6434,18 +6434,22 @@ public class Activity extends ContextThemeWrapper * Request to put this Activity in a mode where the user is locked to the * current task. * - * This will prevent the user from launching other apps, going to settings, - * or reaching the home screen. + * This will prevent the user from launching other apps, going to settings, or reaching the + * home screen. This does not include those apps whose {@link android.R.attr#lockTaskMode} + * values permit launching while locked. * - * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true - * for this component then the app will go directly into Lock Task mode. The user - * will not be able to exit this mode until {@link Activity#stopLockTask()} is called. + * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true or + * lockTaskMode=lockTaskModeAlways for this component then the app will go directly into + * Lock Task mode. The user will not be able to exit this mode until + * {@link Activity#stopLockTask()} is called. * * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns false * then the system will prompt the user with a dialog requesting permission to enter * this mode. When entered through this method the user can exit at any time through * an action described by the request dialog. Calling stopLockTask will also exit the * mode. + * + * @see android.R.attr#lockTaskMode */ public void startLockTask() { try { @@ -6462,6 +6466,14 @@ public class Activity extends ContextThemeWrapper * startLockTask previously. * * This will allow the user to exit this app and move onto other activities. + * <p>Note: This method should only be called when the activity is user-facing. That is, + * between onResume() and onPause(). + * <p>Note: If there are other tasks below this one that are also locked then calling this + * method will immediately finish this task and resume the previous locked one, remaining in + * lockTask mode. + * + * @see android.R.attr#lockTaskMode + * @see ActivityManager#getLockTaskModeState() */ public void stopLockTask() { try { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 907ae26..fe6e4f3 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -771,10 +771,12 @@ final class ApplicationPackageManager extends PackageManager { .getCompatibilityInfo().applicationScale, e); } - if (DEBUG_ICONS) + if (DEBUG_ICONS) { Log.v(TAG, "Getting drawable 0x" + Integer.toHexString(resId) + " from " + r + ": " + dr); + } + return dr; } catch (NameNotFoundException e) { Log.w("PackageManager", "Failure retrieving resources for " + appInfo.packageName); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index e2230da..913159a 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -17,8 +17,10 @@ package android.app; +import android.app.INotificationManagerCallback; import android.app.ITransientNotification; import android.app.Notification; +import android.app.NotificationManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -71,6 +73,7 @@ interface INotificationManager void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); int getInterruptionFilterFromListener(in INotificationListener token); void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); + NotificationManager.Policy.Token getPolicyTokenFromListener(in INotificationListener listener); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); @@ -82,4 +85,8 @@ interface INotificationManager oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); + oneway void requestNotificationPolicyToken(String pkg, in INotificationManagerCallback callback); + 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); } diff --git a/core/java/android/app/INotificationManagerCallback.aidl b/core/java/android/app/INotificationManagerCallback.aidl new file mode 100644 index 0000000..b9414ca --- /dev/null +++ b/core/java/android/app/INotificationManagerCallback.aidl @@ -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 android.app; + +import android.app.NotificationManager; + +/** @hide */ +oneway interface INotificationManagerCallback { + void onPolicyToken(in NotificationManager.Policy.Token token); +} diff --git a/core/java/android/app/NotificationManager.aidl b/core/java/android/app/NotificationManager.aidl new file mode 100644 index 0000000..8380b8d --- /dev/null +++ b/core/java/android/app/NotificationManager.aidl @@ -0,0 +1,20 @@ +/** + * 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; + +parcelable NotificationManager.Policy; +parcelable NotificationManager.Policy.Token;
\ No newline at end of file diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index fa61e18..7133dce 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -16,14 +16,19 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.app.Notification.Builder; +import android.app.NotificationManager.Policy.Token; import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -33,6 +38,8 @@ import android.service.notification.IConditionListener; import android.service.notification.ZenModeConfig; import android.util.Log; +import java.util.Objects; + /** * Class to notify the user of events that happen. This is how you tell * the user that something has happened in the background. {@more} @@ -89,6 +96,14 @@ public class NotificationManager public static final String ACTION_EFFECTS_SUPPRESSOR_CHANGED = "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED"; + /** + * Intent that is broadcast when the state of getNotificationPolicy() changes. + * This broadcast is only sent to registered receivers. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_POLICY_CHANGED + = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + private static INotificationManager sService; /** @hide */ @@ -338,5 +353,293 @@ public class NotificationManager return null; } + /** + * Requests a notification policy token for the calling package. + * + * @param callback required, used to receive the granted token or the deny signal. + * @param handler The handler used when receiving the result. + * If null, the current thread is used. + */ + public void requestNotificationPolicyToken(@NonNull final Policy.Token.RequestCallback callback, + @Nullable Handler handler) { + checkRequired("callback", callback); + final Handler h = handler != null ? handler : new Handler(); + INotificationManager service = getService(); + try { + service.requestNotificationPolicyToken(mContext.getOpPackageName(), + new INotificationManagerCallback.Stub() { + @Override + public void onPolicyToken(final Token token) throws RemoteException { + h.post(new Runnable() { + @Override + public void run() { + if (token != null) { + callback.onTokenGranted(token); + } else { + callback.onTokenDenied(); + } + } + }); + } + }); + } catch (RemoteException e) { + } + } + + /** + * Checks a given notification policy token. + * + * Returns true if the token is still valid for managing policy. + */ + public boolean isNotificationPolicyTokenValid(@NonNull Policy.Token token) { + if (token == null) return false; + INotificationManager service = getService(); + try { + return service.isNotificationPolicyTokenValid(mContext.getOpPackageName(), token); + } catch (RemoteException e) { + } + return false; + } + + /** + * Gets the current notification policy. + * + * @param token A valid notification policy token is required to access the current policy. + */ + public Policy getNotificationPolicy(@NonNull Policy.Token token) { + checkRequired("token", token); + INotificationManager service = getService(); + try { + return service.getNotificationPolicy(token); + } catch (RemoteException e) { + } + return null; + } + + /** + * Sets the current notification policy. + * + * @param token A valid notification policy token is required to modify the current policy. + * @param policy The new desired policy. + */ + public void setNotificationPolicy(@NonNull Policy.Token token, @NonNull Policy policy) { + checkRequired("token", token); + checkRequired("policy", policy); + INotificationManager service = getService(); + try { + service.setNotificationPolicy(token, policy); + } catch (RemoteException e) { + } + } + private Context mContext; + + private static void checkRequired(String name, Object value) { + if (value == null) { + throw new IllegalArgumentException(name + " is required"); + } + } + + /** + * Notification policy configuration. Represents user-preferences for notification + * filtering and prioritization. + */ + public static class Policy implements android.os.Parcelable { + /** Reminder notifications are prioritized. */ + public static final int PRIORITY_CATEGORY_REMINDERS = 1 << 0; + /** Event notifications are prioritized. */ + public static final int PRIORITY_CATEGORY_EVENTS = 1 << 1; + /** Message notifications are prioritized. */ + public static final int PRIORITY_CATEGORY_MESSAGES = 1 << 2; + /** Calls are prioritized. */ + public static final int PRIORITY_CATEGORY_CALLS = 1 << 3; + /** Calls from repeat callers are prioritized. */ + public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4; + + private static final int[] ALL_PRIORITY_CATEGORIES = { + PRIORITY_CATEGORY_REMINDERS, + PRIORITY_CATEGORY_EVENTS, + PRIORITY_CATEGORY_MESSAGES, + PRIORITY_CATEGORY_CALLS, + PRIORITY_CATEGORY_REPEAT_CALLERS, + }; + + /** Any sender is prioritized. */ + public static final int PRIORITY_SENDERS_ANY = 0; + /** Saved contacts are prioritized. */ + public static final int PRIORITY_SENDERS_CONTACTS = 1; + /** Only starred contacts are prioritized. */ + public static final int PRIORITY_SENDERS_STARRED = 2; + + /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */ + public final int priorityCategories; + + /** Notification senders to prioritize. One of: + * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ + public final int prioritySenders; + + public Policy(int priorityCategories, int prioritySenders) { + this.priorityCategories = priorityCategories; + this.prioritySenders = prioritySenders; + } + + /** @hide */ + public Policy(Parcel source) { + this(source.readInt(), source.readInt()); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(priorityCategories); + dest.writeInt(prioritySenders); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(priorityCategories, prioritySenders); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Policy)) return false; + if (o == this) return true; + final Policy other = (Policy) o; + return other.priorityCategories == priorityCategories + && other.prioritySenders == prioritySenders; + } + + @Override + public String toString() { + return "NotificationManager.Policy[" + + "priorityCategories=" + priorityCategoriesToString(priorityCategories) + + ",prioritySenders=" + prioritySendersToString(prioritySenders) + + "]"; + } + + public static String priorityCategoriesToString(int priorityCategories) { + if (priorityCategories == 0) return ""; + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < ALL_PRIORITY_CATEGORIES.length; i++) { + final int priorityCategory = ALL_PRIORITY_CATEGORIES[i]; + if ((priorityCategories & priorityCategory) != 0) { + if (sb.length() > 0) sb.append(','); + sb.append(priorityCategoryToString(priorityCategory)); + } + priorityCategories &= ~priorityCategory; + } + if (priorityCategories != 0) { + if (sb.length() > 0) sb.append(','); + sb.append("PRIORITY_CATEGORY_UNKNOWN_").append(priorityCategories); + } + return sb.toString(); + } + + private static String priorityCategoryToString(int priorityCategory) { + switch (priorityCategory) { + case PRIORITY_CATEGORY_REMINDERS: return "PRIORITY_CATEGORY_REMINDERS"; + case PRIORITY_CATEGORY_EVENTS: return "PRIORITY_CATEGORY_EVENTS"; + case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES"; + case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS"; + case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS"; + default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory; + } + } + + public static String prioritySendersToString(int prioritySenders) { + switch (prioritySenders) { + case PRIORITY_SENDERS_ANY: return "PRIORITY_SENDERS_ANY"; + case PRIORITY_SENDERS_CONTACTS: return "PRIORITY_SENDERS_CONTACTS"; + case PRIORITY_SENDERS_STARRED: return "PRIORITY_SENDERS_STARRED"; + default: return "PRIORITY_SENDERS_UNKNOWN_" + prioritySenders; + } + } + + public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() { + @Override + public Policy createFromParcel(Parcel in) { + return new Policy(in); + } + + @Override + public Policy[] newArray(int size) { + return new Policy[size]; + } + }; + + /** + * Represents a client-specific token required to manage notification policy. + */ + public static class Token implements Parcelable { + private final IBinder mBinder; + + /** @hide */ + public Token(IBinder binder) { + if (binder == null) throw new IllegalArgumentException("Binder required for token"); + mBinder = binder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mBinder); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Token)) return false; + if (o == this) return true; + final Token other = (Token) o; + return Objects.equals(other.mBinder, mBinder); + } + + @Override + public String toString() { + return String.format("NotificationManager.Token[0x%08x]", + System.identityHashCode(mBinder)); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mBinder); + } + + public static final Parcelable.Creator<Token> CREATOR + = new Parcelable.Creator<Token>() { + @Override + public Token createFromParcel(Parcel in) { + return new Token(in.readStrongBinder()); + } + + @Override + public Token[] newArray(int size) { + return new Token[size]; + } + }; + + /** Callback for receiving the result of a token request. */ + public static abstract class RequestCallback { + /** + * Received if the request was granted for this package. + * + * @param token can be used to manage notification policy. + */ + public abstract void onTokenGranted(Policy.Token token); + + /** + * Received if the request was denied for this package. + */ + public abstract void onTokenDenied(); + } + } + } + } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index b3aa6be..4ede5b1 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -55,6 +55,7 @@ import android.location.CountryDetector; import android.location.ICountryDetector; import android.location.ILocationManager; import android.location.LocationManager; +import android.media.AudioDevicesManager; import android.media.AudioManager; import android.media.MediaRouter; import android.media.midi.IMidiManager; @@ -693,6 +694,13 @@ final class SystemServiceRegistry { public RadioManager createService(ContextImpl ctx) { return new RadioManager(ctx); }}); + + registerService(Context.AUDIO_DEVICES_SERVICE, AudioDevicesManager.class, + new CachedServiceFetcher<AudioDevicesManager>() { + @Override + public AudioDevicesManager createService(ContextImpl ctx) { + return new AudioDevicesManager(ctx); + }}); } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a0a6c4c..44760ce 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4210,4 +4210,20 @@ public class DevicePolicyManager { return false; } } + + /** + * Called by device owner to set the enabled state of the status bar. Disabling the status + * bar blocks notifications, quick settings and other screen overlays that allow escaping from + * a single use device. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled New state of the status bar. + */ + public void setStatusBarEnabledState(ComponentName admin, boolean enabled) { + try { + mService.setStatusBarEnabledState(admin, enabled); + } catch (RemoteException re) { + Log.w(TAG, "Failed talking with device policy service", re); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 131b99c..7502e1d 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -223,4 +223,5 @@ interface IDevicePolicyManager { PersistableBundle getOtaPolicy(); boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled); + void setStatusBarEnabledState(in ComponentName who, boolean enabled); } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 2bf267a..d8556a2 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -283,6 +283,7 @@ public abstract class BackupAgent extends ContextWrapper { // all of the ones we will be traversing String rootDir = new File(appInfo.dataDir).getCanonicalPath(); String filesDir = getFilesDir().getCanonicalPath(); + String nobackupDir = getNoBackupFilesDir().getCanonicalPath(); String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); String sharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); String cacheDir = getCacheDir().getCanonicalPath(); @@ -304,6 +305,7 @@ public abstract class BackupAgent extends ContextWrapper { filterSet.add(databaseDir); filterSet.add(sharedPrefsDir); filterSet.add(filesDir); + filterSet.add(nobackupDir); fullBackupFileTree(packageName, FullBackup.ROOT_TREE_TOKEN, rootDir, filterSet, data); // Now do the same for the files dir, db dir, and shared prefs dir diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index 547a2c3..8aeb22d 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -18,6 +18,7 @@ package android.content; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import java.io.PrintWriter; import java.lang.Comparable; @@ -37,6 +38,56 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co private final String mClass; /** + * Create a new component identifier where the class name may be specified + * as either absolute or relative to the containing package. + * + * <p>Relative package names begin with a <code>'.'</code> character. For a package + * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method + * will return a ComponentName with the package <code>"com.example"</code>and class name + * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also + * permitted.</p> + * + * @param pkg the name of the package the component exists in + * @param cls the name of the class inside of <var>pkg</var> that implements + * the component + * @return the new ComponentName + */ + public static ComponentName createRelative(String pkg, String cls) { + if (TextUtils.isEmpty(cls)) { + throw new IllegalArgumentException("class name cannot be empty"); + } + + final String fullName; + if (cls.charAt(0) == '.') { + // Relative to the package. Prepend the package name. + fullName = pkg + cls; + } else { + // Fully qualified package name. + fullName = cls; + } + return new ComponentName(pkg, fullName); + } + + /** + * Create a new component identifier where the class name may be specified + * as either absolute or relative to the containing package. + * + * <p>Relative package names begin with a <code>'.'</code> character. For a package + * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method + * will return a ComponentName with the package <code>"com.example"</code>and class name + * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also + * permitted.</p> + * + * @param pkg a Context for the package implementing the component + * @param cls the name of the class inside of <var>pkg</var> that implements + * the component + * @return the new ComponentName + */ + public static ComponentName createRelative(Context pkg, String cls) { + return createRelative(pkg.getPackageName(), cls); + } + + /** * Create a new component identifier. * * @param pkg The name of the package that the component exists in. Can diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e5e55d6..0cbf960 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3077,6 +3077,15 @@ public abstract class Context { */ public static final String RADIO_SERVICE = "radio"; + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.media.AudioDevicesManager} for handling device enumeration & notification. + * + * @see #getSystemService + * @see android.media.AudioDevicesManager + */ + public static final String AUDIO_DEVICES_SERVICE = "audio_devices_manager"; + /** * Determine whether the given permission is allowed for a particular diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 4723c0d..8d82aa2 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -609,7 +609,7 @@ public class ActivityInfo extends ComponentInfo * attribute. */ public int configChanges; - + /** * The desired soft input mode for this activity's main window. * Set from the {@link android.R.attr#windowSoftInputMode} attribute @@ -648,6 +648,37 @@ public class ActivityInfo extends ComponentInfo */ public boolean resizeable; + /** @hide */ + public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0; + /** @hide */ + public static final int LOCK_TASK_LAUNCH_MODE_NEVER = 1; + /** @hide */ + public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2; + /** @hide */ + public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3; + + /** @hide */ + public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) { + switch (lockTaskLaunchMode) { + case LOCK_TASK_LAUNCH_MODE_DEFAULT: + return "LOCK_TASK_LAUNCH_MODE_DEFAULT"; + case LOCK_TASK_LAUNCH_MODE_NEVER: + return "LOCK_TASK_LAUNCH_MODE_NEVER"; + case LOCK_TASK_LAUNCH_MODE_ALWAYS: + return "LOCK_TASK_LAUNCH_MODE_ALWAYS"; + case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: + return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED"; + default: + return "unknown=" + lockTaskLaunchMode; + } + } + /** + * Value indicating if the activity is to be locked at startup. Takes on the values from + * {@link android.R.attr#lockTaskMode}. + * @hide + */ + public int lockTaskLaunchMode; + public ActivityInfo() { } @@ -665,13 +696,15 @@ public class ActivityInfo extends ComponentInfo uiOptions = orig.uiOptions; parentActivityName = orig.parentActivityName; maxRecents = orig.maxRecents; + resizeable = orig.resizeable; + lockTaskLaunchMode = orig.lockTaskLaunchMode; } - + /** * Return the theme resource identifier to use for this activity. If * the activity defines a theme, that is used; else, the application * theme is used. - * + * * @return The theme associated with this activity. */ public final int getThemeResource() { @@ -709,7 +742,8 @@ public class ActivityInfo extends ComponentInfo if (uiOptions != 0) { pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions)); } - pw.println(prefix + "resizeable=" + resizeable); + pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode=" + + lockTaskLaunchModeToString(lockTaskLaunchMode)); super.dumpBack(pw, prefix); } @@ -739,6 +773,7 @@ public class ActivityInfo extends ComponentInfo dest.writeInt(persistableMode); dest.writeInt(maxRecents); dest.writeInt(resizeable ? 1 : 0); + dest.writeInt(lockTaskLaunchMode); } public static final Parcelable.Creator<ActivityInfo> CREATOR @@ -767,5 +802,6 @@ public class ActivityInfo extends ComponentInfo persistableMode = source.readInt(); maxRecents = source.readInt(); resizeable = (source.readInt() == 1); + lockTaskLaunchMode = source.readInt(); } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 763a017..40f4e8f 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3159,6 +3159,9 @@ public class PackageParser { R.styleable.AndroidManifestActivity_screenOrientation, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } + + a.info.lockTaskLaunchMode = + sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0); } else { a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; a.info.configChanges = 0; @@ -4655,7 +4658,7 @@ public class PackageParser { private static boolean copyNeeded(int flags, Package p, PackageUserState state, Bundle metaData, int userId) { - if (userId != 0) { + if (userId != UserHandle.USER_OWNER) { // We always need to copy for other users, since we need // to fix up the uid. return true; @@ -4737,11 +4740,9 @@ public class PackageParser { // Make shallow copy so we can store the metadata/libraries safely ApplicationInfo ai = new ApplicationInfo(p.applicationInfo); - if (userId != 0) { - ai.uid = UserHandle.getUid(userId, ai.uid); - ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId) - .getAbsolutePath(); - } + ai.uid = UserHandle.getUid(userId, ai.uid); + ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId) + .getAbsolutePath(); if ((flags & PackageManager.GET_META_DATA) != 0) { ai.metaData = p.mAppMetaData; } @@ -4766,11 +4767,9 @@ public class PackageParser { // This is only used to return the ResolverActivity; we will just always // make a copy. ai = new ApplicationInfo(ai); - if (userId != 0) { - ai.uid = UserHandle.getUid(userId, ai.uid); - ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId) - .getAbsolutePath(); - } + ai.uid = UserHandle.getUid(userId, ai.uid); + ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId) + .getAbsolutePath(); if (state.stopped) { ai.flags |= ApplicationInfo.FLAG_STOPPED; } else { diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 49f6513..d88594d 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1803,8 +1803,6 @@ public class Camera { public Point mouth = null; } - // Error codes match the enum in include/ui/Camera.h - /** * Unspecified camera error. * @see Camera.ErrorCallback @@ -1812,6 +1810,12 @@ public class Camera { public static final int CAMERA_ERROR_UNKNOWN = 1; /** + * Camera was disconnected due to use by higher priority user. + * @see Camera.ErrorCallback + */ + public static final int CAMERA_ERROR_EVICTED = 2; + + /** * Media server died. In this case, the application must release the * Camera object and instantiate a new one. * @see Camera.ErrorCallback diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 6b6f026..31e6e25 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -18,6 +18,7 @@ package android.hardware.camera2; import android.os.Handler; import android.view.Surface; + import java.util.List; @@ -69,6 +70,61 @@ public abstract class CameraCaptureSession implements AutoCloseable { public abstract CameraDevice getDevice(); /** + * <p>Pre-allocate all buffers for an output Surface.</p> + * + * <p>Normally, the image buffers for a given output Surface are allocated on-demand, + * to minimize startup latency and memory overhead.</p> + * + * <p>However, in some cases, it may be desirable for the buffers to be allocated before + * any requests targeting the Surface are actually submitted to the device. Large buffers + * may take some time to allocate, which can result in delays in submitting requests until + * sufficient buffers are allocated to reach steady-state behavior. Such delays can cause + * bursts to take longer than desired, or cause skips or stutters in preview output.</p> + * + * <p>The prepare() method can be used to perform this preallocation. It may only be called for + * a given output Surface before that Surface is used as a target for a request. The number of + * buffers allocated is the sum of the count needed by the consumer providing the output + * Surface, and the maximum number needed by the camera device to fill its pipeline. Since this + * may be a larger number than what is actually required for steady-state operation, using + * prepare may result in higher memory consumption than the normal on-demand behavior results + * in. Prepare() will also delay the time to first output to a given Surface, in exchange for + * smoother frame rate once the allocation is complete.</p> + * + * <p>For example, an application that creates an + * {@link android.media.ImageReader#newInstance ImageReader} with a maxImages argument of 10, + * but only uses 3 simultaneous Images at once would normally only cause those 3 images to be + * allocated (plus what is needed by the camera device for smooth operation). But using + * prepare() on the ImageReader Surface will result in all 10 Images being allocated. So + * applications using this method should take care to request only the number of buffers + * actually necessary for their application.</p> + * + * <p>If the same output Surface is used in consecutive sessions (without closing the first + * session explicitly), then its already-allocated buffers are carried over, and if it was + * used as a target of a capture request in the first session, prepare cannot be called on it + * in the second session.</p> + * + * <p>Once allocation is complete, {@link StateCallback#onSurfacePrepared} will be invoked with + * the Surface provided to this method. Between the prepare call and the onSurfacePrepared call, + * the Surface provided to prepare must not be used as a target of a CaptureRequest submitted + * to this session.</p> + * + * @param surface the output Surface for which buffers should be pre-allocated. Must be one of + * the output Surfaces used to create this session. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException if the Surface is invalid, not part of this Session, or has + * already been used as a target of a CaptureRequest in this + * session or immediately prior sessions. + * + * @see StateCallback#onSurfacePrepared + */ + public abstract void prepare(Surface surface) throws CameraAccessException; + + /** * <p>Submit a request for an image to be captured by the camera device.</p> * * <p>The request defines all the parameters for capturing the single image, @@ -110,9 +166,10 @@ public abstract class CameraCaptureSession implements AutoCloseable { * was explicitly closed, a new session has been created * or the camera device has been closed. * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not - * configured as outputs for this session. Or if a reprocess + * configured as outputs for this session; or a reprocess * capture request is submitted in a non-reprocessible capture - * session. Or if the handler is + * session; or the capture targets a Surface in the middle + * of being {@link #prepare prepared}; or the handler is * null, the listener is not null, and the calling thread has * no looper. * @@ -164,13 +221,15 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalStateException if this session is no longer active, either because the session * was explicitly closed, a new session has been created * or the camera device has been closed. - * @throws IllegalArgumentException If the requests target no Surfaces, or target Surfaces not - * currently configured as outputs. Or if a reprocess + * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target + * Surfaces not currently configured as outputs; or a reprocess * capture request is submitted in a non-reprocessible capture - * session. Or if the list of requests contains both requests - * to capture images from the camera and reprocess capture - * requests. Or if the handler is null, the listener is not - * null, and the calling thread has no looper. + * session; or the list of requests contains both requests to + * capture images from the camera and reprocess capture + * requests; or one of the captures targets a Surface in the + * middle of being {@link #prepare prepared}; or if the handler + * is null, the listener is not null, and the calling thread + * has no looper. * * @see #capture * @see #setRepeatingRequest @@ -230,11 +289,12 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalStateException if this session is no longer active, either because the session * was explicitly closed, a new session has been created * or the camera device has been closed. - * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces that are - * not currently configured as outputs. Or if the request is - * a reprocess capture request. Or if the handler is null, the - * listener is not null, and the calling thread has no looper. - * Or if no requests were passed in. + * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces + * that are not currently configured as outputs; or the request + * is a reprocess capture request; or the capture targets a + * Surface in the middle of being {@link #prepare prepared}; or + * the handler is null, the listener is not null, and the + * calling thread has no looper; or no requests were passed in. * * @see #capture * @see #captureBurst @@ -299,11 +359,13 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalStateException if this session is no longer active, either because the session * was explicitly closed, a new session has been created * or the camera device has been closed. - * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces not - * currently configured as outputs. Or if one of the requests - * is a reprocess capture request. Or if the handler is null, - * the listener is not null, and the calling thread has no - * looper. Or if no requests were passed in. + * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces + * not currently configured as outputs; or one of the requests + * is a reprocess capture request; or one of the captures + * targets a Surface in the middle of being + * {@link #prepare prepared}; or the handler is null, the + * listener is not null, and the calling thread has no looper; + * or no requests were passed in. * * @see #capture * @see #captureBurst @@ -514,6 +576,25 @@ public abstract class CameraCaptureSession implements AutoCloseable { public void onClosed(CameraCaptureSession session) { // default empty implementation } + + /** + * This method is called when the buffer pre-allocation for an output Surface is complete. + * + * <p>Buffer pre-allocation for an output Surface is started by the {@link #prepare} call. + * While allocation is underway, the Surface must not be used as a capture target. + * Once this callback fires, the output Surface provided can again be used as a target for + * a capture request.</p> + * + * <p>In case of a error during pre-allocation (such as running out of suitable memory), + * this callback is still invoked after the error is encountered, though some buffers may + * not have been successfully pre-allocated.</p> + * + * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param surface the Surface that was used with the {@link #prepare} call. + */ + public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { + // default empty implementation + } } /** diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl index ca0935c..151c918 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl @@ -31,4 +31,5 @@ interface ICameraDeviceCallbacks oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp); oneway void onResultReceived(in CameraMetadataNative result, in CaptureResultExtras resultExtras); + oneway void onPrepared(int streamId); } diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl index 23bfa66..375b310 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl @@ -98,4 +98,6 @@ interface ICameraDeviceUser int waitUntilIdle(); int flush(out LongParcelable lastFrameNumber); + + int prepare(int streamId); } diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java index f0217ac..dac2ef8 100644 --- a/core/java/android/hardware/camera2/impl/CallbackProxies.java +++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java @@ -23,6 +23,7 @@ import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.dispatch.Dispatchable; import android.hardware.camera2.dispatch.MethodNameInvoker; +import android.view.Surface; import static com.android.internal.util.Preconditions.*; @@ -175,6 +176,12 @@ public class CallbackProxies { public void onClosed(CameraCaptureSession session) { mProxy.invoke("onClosed", session); } + + @Override + public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { + mProxy.invoke("onSurfacePrepared", session, surface); + } + } private CallbackProxies() { diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index fb5b13c..c74204d 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -144,6 +144,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } @Override + public void prepare(Surface surface) throws CameraAccessException { + mDeviceImpl.prepare(surface); + } + + @Override public synchronized int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { if (request == null) { @@ -589,6 +594,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } } } + + @Override + public void onSurfacePrepared(Surface surface) { + if (VERBOSE) Log.v(TAG, mIdString + "onPrepared"); + mStateCallback.onSurfacePrepared(session, surface); + } + }; } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 91388c3..1e680dfd 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -605,6 +605,29 @@ public class CameraDeviceImpl extends CameraDevice { } } + public void prepare(Surface surface) throws CameraAccessException { + synchronized(mInterfaceLock) { + int streamId = -1; + for (int i = 0; i < mConfiguredOutputs.size(); i++) { + if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { + streamId = mConfiguredOutputs.keyAt(i); + break; + } + } + if (streamId == -1) { + throw new IllegalArgumentException("Surface is not part of this session"); + } + try { + mRemoteDevice.prepare(streamId); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + return; + } + } + } + public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { if (DEBUG) { @@ -1056,6 +1079,14 @@ public class CameraDeviceImpl extends CameraDevice { public void onIdle(CameraDevice camera) { // Default empty implementation } + + /** + * The method called when the camera device has finished preparing + * an output Surface + */ + public void onSurfacePrepared(Surface surface) { + // Default empty implementation + } } static class CaptureCallbackHolder { @@ -1643,6 +1674,31 @@ public class CameraDeviceImpl extends CameraDevice { } } + @Override + public void onPrepared(int streamId) { + final OutputConfiguration output; + final StateCallbackKK sessionCallback; + + if (DEBUG) { + Log.v(TAG, "Stream " + streamId + " is prepared"); + } + + synchronized(mInterfaceLock) { + output = mConfiguredOutputs.get(streamId); + sessionCallback = mSessionStateCallback; + } + + if (sessionCallback == null) return; + + if (output == null) { + Log.w(TAG, "onPrepared invoked for unknown output Surface"); + return; + } + final Surface surface = output.getSurface(); + + sessionCallback.onSurfacePrepared(surface); + } + /** * Called by onDeviceError for handling single-capture failures. */ diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index 4cd9414..abe26ea 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -252,6 +252,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override + public void onPrepared(int streamId) { + // TODO + } + + @Override public IBinder asBinder() { // This is solely intended to be used for in-process binding. return null; @@ -617,6 +622,19 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { return CameraBinderDecorator.NO_ERROR; } + public int prepare(int streamId) { + if (DEBUG) { + Log.d(TAG, "prepare called."); + } + if (mLegacyDevice.isClosed()) { + Log.e(TAG, "Cannot prepare stream, device has been closed."); + return CameraBinderDecorator.ENODEV; + } + + // TODO: Implement and fire callback + return CameraBinderDecorator.NO_ERROR; + } + @Override public IBinder asBinder() { // This is solely intended to be used for in-process binding. diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 5f85e7d..0d7b261 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -56,7 +56,7 @@ public class FingerprintManager { private static final boolean DEBUG = true; private static final int MSG_ENROLL_RESULT = 100; private static final int MSG_ACQUIRED = 101; - private static final int MSG_PROCESSED = 102; + private static final int MSG_AUTHENTICATED = 102; private static final int MSG_ERROR = 103; private static final int MSG_REMOVED = 104; @@ -103,6 +103,11 @@ public class FingerprintManager { */ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; + /** + * The operation was canceled because the API is locked out due to too many attempts. + */ + public static final int FINGERPRINT_ERROR_LOCKOUT = 7; + /** * Hardware vendors may extend this list if there are conditions that do not fall under one of * the above categories. Vendors are responsible for providing error strings for these errors. @@ -169,15 +174,9 @@ public class FingerprintManager { private Fingerprint mRemovalFingerprint; private class OnEnrollCancelListener implements OnCancelListener { - private long mChallenge; - - public OnEnrollCancelListener(long challenge) { - mChallenge = challenge; - } - @Override public void onCancel() { - cancelEnrollment(mChallenge); + cancelEnrollment(); } } @@ -200,12 +199,12 @@ public class FingerprintManager { */ public static class CryptoObject { - CryptoObject(Signature signature) { + public CryptoObject(Signature signature) { mSignature = signature; mCipher = null; } - CryptoObject(Cipher cipher) { + public CryptoObject(Cipher cipher) { mCipher = cipher; mSignature = null; } @@ -437,14 +436,14 @@ public class FingerprintManager { * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at * which point the object is no longer valid. The operation can be canceled by using the * provided cancel object. - * @param challenge a unique id provided by a recent verification of device credentials - * (e.g. pin, pattern or password). + * @param token a unique token provided by a recent creation or verification of device + * credentials (e.g. pin, pattern or password). * @param cancel an object that can be used to cancel enrollment * @param callback an object to receive enrollment events * @param flags optional flags * @hide */ - public void enroll(long challenge, CancellationSignal cancel, EnrollmentCallback callback, + public void enroll(byte [] token, CancellationSignal cancel, EnrollmentCallback callback, int flags) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); @@ -455,13 +454,13 @@ public class FingerprintManager { Log.w(TAG, "enrollment already canceled"); return; } else { - cancel.setOnCancelListener(new OnEnrollCancelListener(challenge)); + cancel.setOnCancelListener(new OnEnrollCancelListener()); } } if (mService != null) try { mEnrollmentCallback = callback; - mService.enroll(mToken, challenge, getCurrentUserId(), mServiceReceiver, flags); + mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); if (callback != null) { @@ -536,9 +535,9 @@ public class FingerprintManager { * * @hide */ - public List<Fingerprint> getEnrolledFingerprints() { + public List<Fingerprint> getEnrolledFingerprints(int userId) { if (mService != null) try { - return mService.getEnrolledFingerprints(getCurrentUserId()); + return mService.getEnrolledFingerprints(userId); } catch (RemoteException e) { Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e); } @@ -546,11 +545,34 @@ public class FingerprintManager { } /** - * Determine if fingerprint hardware is present and functional. - * @return true if hardware is present and functional, false otherwise. + * Obtain the list of enrolled fingerprints templates. + * @return list of current fingerprint items * * @hide */ + public List<Fingerprint> getEnrolledFingerprints() { + return getEnrolledFingerprints(UserHandle.myUserId()); + } + + /** + * Determine if there is at least one fingerprint enrolled. + * + * @return true if at least one fingerprint is enrolled, false otherwise + */ + public boolean hasEnrolledFingerprints() { + if (mService != null) try { + return mService.hasEnrolledFingerprints(UserHandle.myUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e); + } + return false; + } + + /** + * Determine if fingerprint hardware is present and functional. + * + * @return true if hardware is present and functional, false otherwise. + */ public boolean isHardwareDetected() { if (mService != null) { try { @@ -574,8 +596,8 @@ public class FingerprintManager { case MSG_ACQUIRED: sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */); break; - case MSG_PROCESSED: - sendProcessedResult((Fingerprint) msg.obj); + case MSG_AUTHENTICATED: + sendAuthenticatedResult((Fingerprint) msg.obj); break; case MSG_ERROR: sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */); @@ -617,7 +639,7 @@ public class FingerprintManager { } } - private void sendProcessedResult(Fingerprint fp) { + private void sendAuthenticatedResult(Fingerprint fp) { if (mAuthenticationCallback != null) { if (fp.getFingerId() == 0 && fp.getGroupId() == 0) { // Fingerprint template valid but doesn't match one in database @@ -667,7 +689,7 @@ public class FingerprintManager { mRemovalCallback = null; } - private void cancelEnrollment(long challenge) { + private void cancelEnrollment() { if (mService != null) try { mService.cancelEnrollment(mToken); } catch (RemoteException e) { @@ -695,8 +717,11 @@ public class FingerprintManager { return mContext.getString( com.android.internal.R.string.fingerprint_error_no_space); case FINGERPRINT_ERROR_TIMEOUT: - return mContext.getString( - com.android.internal.R.string.fingerprint_error_timeout); + return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout); + case FINGERPRINT_ERROR_CANCELED: + return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled); + case FINGERPRINT_ERROR_LOCKOUT: + return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout); default: if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) { int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE; @@ -753,8 +778,8 @@ public class FingerprintManager { mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget(); } - public void onProcessed(long deviceId, int fingerId, int groupId) { - mHandler.obtainMessage(MSG_PROCESSED, + public void onAuthenticated(long deviceId, int fingerId, int groupId) { + mHandler.obtainMessage(MSG_AUTHENTICATED, new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 2fcb20e..51a0e4c 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -33,7 +33,7 @@ interface IFingerprintService { void cancelAuthentication(IBinder token); // Start fingerprint enrollment - void enroll(IBinder token, long challenge, int groupId, IFingerprintServiceReceiver receiver, + void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver, int flags); // Cancel enrollment in progress @@ -54,6 +54,9 @@ interface IFingerprintService { // Get a pre-enrollment authentication token long preEnroll(IBinder token); + // Determine if a user has at least one enrolled fingerprint + boolean hasEnrolledFingerprints(int groupId); + // Gets the number of hardware devices // int getHardwareDeviceCount(); diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index e82395f..a2d74b8 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -25,7 +25,7 @@ import android.os.UserHandle; oneway interface IFingerprintServiceReceiver { void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining); void onAcquired(long deviceId, int acquiredInfo); - void onProcessed(long deviceId, int fingerId, int groupId); + void onAuthenticated(long deviceId, int fingerId, int groupId); void onError(long deviceId, int error); void onRemoved(long deviceId, int fingerId, int groupId); } diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java index 5d40e94..ee2d43c 100644 --- a/core/java/android/hardware/location/GeofenceHardwareImpl.java +++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java @@ -41,6 +41,7 @@ import java.util.Iterator; public final class GeofenceHardwareImpl { private static final String TAG = "GeofenceHardwareImpl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final int FIRST_VERSION_WITH_CAPABILITIES = 2; private final Context mContext; private static GeofenceHardwareImpl sInstance; @@ -54,6 +55,7 @@ public final class GeofenceHardwareImpl { private IFusedGeofenceHardware mFusedService; private IGpsGeofenceHardware mGpsService; private int mCapabilities; + private int mVersion = 1; private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS]; @@ -145,8 +147,10 @@ public final class GeofenceHardwareImpl { private void updateFusedHardwareAvailability() { boolean fusedSupported; try { + final boolean hasGnnsCapabilities = (mVersion < FIRST_VERSION_WITH_CAPABILITIES) + || (mCapabilities & CAPABILITY_GNSS) != 0; fusedSupported = (mFusedService != null - ? mFusedService.isSupported() && (mCapabilities & CAPABILITY_GNSS) != 0 + ? mFusedService.isSupported() && hasGnnsCapabilities : false); } catch (RemoteException e) { Log.e(TAG, "RemoteException calling LocationManagerService"); @@ -177,6 +181,11 @@ public final class GeofenceHardwareImpl { updateFusedHardwareAvailability(); } + public void setVersion(int version) { + mVersion = version; + updateFusedHardwareAvailability(); + } + public void setFusedGeofenceHardware(IFusedGeofenceHardware service) { if(mFusedService == null) { mFusedService = service; @@ -230,7 +239,12 @@ public final class GeofenceHardwareImpl { case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: return CAPABILITY_GNSS; case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE: - return mCapabilities; + if (mVersion >= FIRST_VERSION_WITH_CAPABILITIES) { + return mCapabilities; + } + // This was the implied capability on old FLP HAL versions that didn't + // have the capability callback. + return CAPABILITY_GNSS; } break; } diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl index 3de766a..2ea4d23 100644 --- a/core/java/android/hardware/location/IFusedLocationHardware.aidl +++ b/core/java/android/hardware/location/IFusedLocationHardware.aidl @@ -121,4 +121,9 @@ interface IFusedLocationHardware { * of the locations returned in this call. */ void flushBatchedLocations() = 11; + + /** + * Returns the version of this FLP HAL implementation. + */ + int getVersion() = 12; } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 8fb4e76..ce1b01e 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -286,6 +286,14 @@ public class ConnectivityManager { public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal"; /** + * Action used to display a dialog that asks the user whether to connect to a network that is + * not validated. This intent is used to start the dialog in settings via startActivity. + * + * @hide + */ + public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED"; + + /** * The absence of a connection type. * @hide */ @@ -2463,6 +2471,29 @@ public class ConnectivityManager { } /** + * Informs the system whether it should switch to {@code network} regardless of whether it is + * validated or not. If {@code accept} is true, and the network was explicitly selected by the + * user (e.g., by selecting a Wi-Fi network in the Settings app), then the network will become + * the system default network regardless of any other network that's currently connected. If + * {@code always} is true, then the choice is remembered, so that the next time the user + * connects to this network, the system will switch to it. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} + * + * @param network The network to accept. + * @param accept Whether to accept the network even if unvalidated. + * @param always Whether to remember this choice in the future. + * + * @hide + */ + public void setAcceptUnvalidated(Network network, boolean accept, boolean always) { + try { + mService.setAcceptUnvalidated(network, accept, always); + } catch (RemoteException e) {} + } + + /** * Resets all connectivity manager settings back to factory defaults. * @hide */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 43dd7c9..055f1ab 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -155,6 +155,8 @@ interface IConnectivityManager void releaseNetworkRequest(in NetworkRequest networkRequest); + void setAcceptUnvalidated(in Network network, boolean accept, boolean always); + int getRestoreDefaultNetworkDelay(int networkType); boolean addVpnAddress(String address, int prefixLength); diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 65d325a1..67ecb5d 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -340,6 +340,35 @@ public class Network implements Parcelable { } } + /** + * Returns a handle representing this {@code Network}, for use with the NDK API. + */ + public long getNetworkHandle() { + // The network handle is explicitly not the same as the netId. + // + // The netId is an implementation detail which might be changed in the + // future, or which alone (i.e. in the absence of some additional + // context) might not be sufficient to fully identify a Network. + // + // As such, the intention is to prevent accidental misuse of the API + // that might result if a developer assumed that handles and netIds + // were identical and passing a netId to a call expecting a handle + // "just worked". Such accidental misuse, if widely deployed, might + // prevent future changes to the semantics of the netId field or + // inhibit the expansion of state required for Network objects. + // + // This extra layer of indirection might be seen as paranoia, and might + // never end up being necessary, but the added complexity is trivial. + // At some future date it may be desirable to realign the handle with + // Multiple Provisioning Domains API recommendations, as made by the + // IETF mif working group. + // + // The HANDLE_MAGIC value MUST be kept in sync with the corresponding + // value in the native/android/net.c NDK implementation. + final long HANDLE_MAGIC = 0xfacade; + return (((long) netId) << 32) | HANDLE_MAGIC; + } + // implement the Parcelable interface public int describeContents() { return 0; diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index a955bbb..3f2dd28 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -108,7 +108,7 @@ public abstract class NetworkAgent extends Handler { public static final int EVENT_UID_RANGES_REMOVED = BASE + 6; /** - * Sent by ConnectivitySerice to the NetworkAgent to inform the agent of the + * Sent by ConnectivityService to the NetworkAgent to inform the agent of the * networks status - whether we could use the network or could not, due to * either a bad network configuration (no internet link) or captive portal. * @@ -123,9 +123,21 @@ public abstract class NetworkAgent extends Handler { * Sent by the NetworkAgent to ConnectivityService to indicate this network was * explicitly selected. This should be sent before the NetworkInfo is marked * CONNECTED so it can be given special treatment at that time. + * + * obj = boolean indicating whether to use this network even if unvalidated */ public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8; + /** + * Sent by ConnectivityService to the NetworkAgent to inform the agent of + * whether the network should in the future be used even if not validated. + * This decision is made by the user, but it is the network transport's + * responsibility to remember it. + * + * arg1 = 1 if true, 0 if false + */ + public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9; + public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score) { this(looper, context, logTag, ni, nc, lp, score, null); @@ -195,6 +207,9 @@ public abstract class NetworkAgent extends Handler { networkStatus(msg.arg1); break; } + case CMD_SAVE_ACCEPT_UNVALIDATED: { + saveAcceptUnvalidated(msg.arg1 != 0); + } } } @@ -262,10 +277,16 @@ public abstract class NetworkAgent extends Handler { /** * Called by the bearer to indicate this network was manually selected by the user. * This should be called before the NetworkInfo is marked CONNECTED so that this - * Network can be given special treatment at that time. + * Network can be given special treatment at that time. If {@code acceptUnvalidated} is + * {@code true}, then the system will switch to this network. If it is {@code false} and the + * network cannot be validated, the system will ask the user whether to switch to this network. + * If the user confirms and selects "don't ask again", then the system will call + * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever + * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement + * {@link #saveAcceptUnvalidated} to respect the user's choice. */ - public void explicitlySelected() { - queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, 0); + public void explicitlySelected(boolean acceptUnvalidated) { + queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated); } /** @@ -294,6 +315,16 @@ public abstract class NetworkAgent extends Handler { protected void networkStatus(int status) { } + /** + * Called when the user asks to remember the choice to use this network even if unvalidated. + * The transport is responsible for remembering the choice, and the next time the user connects + * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}. + * This method will only be called if {@link #explicitlySelected} was called with + * {@code acceptUnvalidated} set to {@code false}. + */ + protected void saveAcceptUnvalidated(boolean accept) { + } + protected void log(String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); } diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java index b92c9e3..5511a24 100644 --- a/core/java/android/net/NetworkMisc.java +++ b/core/java/android/net/NetworkMisc.java @@ -45,6 +45,13 @@ public class NetworkMisc implements Parcelable { public boolean explicitlySelected; /** + * Set if the user desires to use this network even if it is unvalidated. This field has meaning + * only if {#link explicitlySelected} is true. If it is, this field must also be set to the + * appropriate value based on previous user choice. + */ + public boolean acceptUnvalidated; + + /** * For mobile networks, this is the subscriber ID (such as IMSI). */ public String subscriberId; @@ -56,6 +63,7 @@ public class NetworkMisc implements Parcelable { if (nm != null) { allowBypass = nm.allowBypass; explicitlySelected = nm.explicitlySelected; + acceptUnvalidated = nm.acceptUnvalidated; subscriberId = nm.subscriberId; } } @@ -69,6 +77,7 @@ public class NetworkMisc implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(allowBypass ? 1 : 0); out.writeInt(explicitlySelected ? 1 : 0); + out.writeInt(acceptUnvalidated ? 1 : 0); out.writeString(subscriberId); } @@ -78,6 +87,7 @@ public class NetworkMisc implements Parcelable { NetworkMisc networkMisc = new NetworkMisc(); networkMisc.allowBypass = in.readInt() != 0; networkMisc.explicitlySelected = in.readInt() != 0; + networkMisc.acceptUnvalidated = in.readInt() != 0; networkMisc.subscriberId = in.readString(); return networkMisc; } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 00b2ee3..f10e530 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -262,7 +263,7 @@ public final class ApduServiceInfo implements Parcelable { * for that category. * @return List of AIDs registered by the service */ - public ArrayList<String> getAids() { + public List<String> getAids() { final ArrayList<String> aids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { aids.addAll(group.aids); @@ -270,6 +271,18 @@ public final class ApduServiceInfo implements Parcelable { return aids; } + public List<String> getPrefixAids() { + final ArrayList<String> prefixAids = new ArrayList<String>(); + for (AidGroup group : getAidGroups()) { + for (String aid : group.aids) { + if (aid.endsWith("*")) { + prefixAids.add(aid); + } + } + } + return prefixAids; + } + /** * Returns the registered AID group for this category. */ diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index b4a4624..64562a4 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -187,6 +187,13 @@ public class Binder implements IBinder { } /** + * Call blocks until the number of executing binder threads is less + * than the maximum number of binder threads allowed for this process. + * @hide + */ + public static final native void blockUntilThreadAvailable(); + + /** * Default constructor initializes the object. */ public Binder() { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 360e5c68..75b1101 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1181,7 +1181,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns a map of the names/values of the runtime statistics - * that {@link #getRuntimeStat()} supports. + * that {@link #getRuntimeStat(String)} supports. * * @return a map of the names/values of the supported runtime statistics. * @hide diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 4704b67..dc96640 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -16,6 +16,7 @@ package android.os.storage; +import android.annotation.NonNull; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; @@ -33,6 +34,8 @@ import java.io.CharArrayWriter; * @hide */ public class DiskInfo implements Parcelable { + public static final String EXTRA_DISK_ID = "android.os.storage.extra.DISK_ID"; + public static final int FLAG_ADOPTABLE = 1 << 0; public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; public static final int FLAG_SD = 1 << 2; @@ -42,7 +45,7 @@ public class DiskInfo implements Parcelable { public final int flags; public long size; public String label; - public String[] volumes; + public String[] volumeIds; public DiskInfo(String id, int flags) { this.id = Preconditions.checkNotNull(id); @@ -54,7 +57,11 @@ public class DiskInfo implements Parcelable { flags = parcel.readInt(); size = parcel.readLong(); label = parcel.readString(); - volumes = parcel.readStringArray(); + volumeIds = parcel.readStringArray(); + } + + public @NonNull String getId() { + return id; } public String getDescription() { @@ -68,6 +75,18 @@ public class DiskInfo implements Parcelable { } } + public boolean isSd() { + return (flags & FLAG_SD) != 0; + } + + public boolean isUsb() { + return (flags & FLAG_USB) != 0; + } + + public boolean isAdoptable() { + return (flags & FLAG_ADOPTABLE) != 0; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); @@ -82,7 +101,7 @@ public class DiskInfo implements Parcelable { pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags)); pw.printPair("size", size); pw.printPair("label", label); - pw.printPair("volumes", volumes); + pw.printPair("volumeIds", volumeIds); pw.decreaseIndent(); pw.println(); } @@ -122,6 +141,6 @@ public class DiskInfo implements Parcelable { parcel.writeInt(this.flags); parcel.writeLong(size); parcel.writeString(label); - parcel.writeStringArray(volumes); + parcel.writeStringArray(volumeIds); } } diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 10ffd48..0a8187e 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -923,12 +923,13 @@ public interface IMountService extends IInterface { } @Override - public VolumeInfo[] getVolumes() throws RemoteException { + public VolumeInfo[] getVolumes(int _flags) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); VolumeInfo[] _result; try { _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(_flags); mRemote.transact(Stub.TRANSACTION_getVolumes, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArray(VolumeInfo.CREATOR); @@ -1029,6 +1030,39 @@ public interface IMountService extends IInterface { _data.recycle(); } } + + @Override + public void setVolumeNickname(String volId, String nickname) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(volId); + _data.writeString(nickname); + mRemote.transact(Stub.TRANSACTION_setVolumeNickname, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override + public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(volId); + _data.writeInt(flags); + _data.writeInt(mask); + mRemote.transact(Stub.TRANSACTION_setVolumeUserFlags, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } } private static final String DESCRIPTOR = "IMountService"; @@ -1132,6 +1166,9 @@ public interface IMountService extends IInterface { static final int TRANSACTION_partitionPrivate = IBinder.FIRST_CALL_TRANSACTION + 50; static final int TRANSACTION_partitionMixed = IBinder.FIRST_CALL_TRANSACTION + 51; + static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52; + static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1566,7 +1603,8 @@ public interface IMountService extends IInterface { } case TRANSACTION_getVolumes: { data.enforceInterface(DESCRIPTOR); - VolumeInfo[] volumes = getVolumes(); + int _flags = data.readInt(); + VolumeInfo[] volumes = getVolumes(_flags); reply.writeNoException(); reply.writeTypedArray(volumes, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); return true; @@ -1614,6 +1652,23 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_setVolumeNickname: { + data.enforceInterface(DESCRIPTOR); + String volId = data.readString(); + String nickname = data.readString(); + setVolumeNickname(volId, nickname); + reply.writeNoException(); + return true; + } + case TRANSACTION_setVolumeUserFlags: { + data.enforceInterface(DESCRIPTOR); + String volId = data.readString(); + int _flags = data.readInt(); + int _mask = data.readInt(); + setVolumeUserFlags(volId, _flags, _mask); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -1902,7 +1957,7 @@ public interface IMountService extends IInterface { public void waitForAsecScan() throws RemoteException; public DiskInfo[] getDisks() throws RemoteException; - public VolumeInfo[] getVolumes() throws RemoteException; + public VolumeInfo[] getVolumes(int flags) throws RemoteException; public void mount(String volId) throws RemoteException; public void unmount(String volId) throws RemoteException; @@ -1911,4 +1966,7 @@ public interface IMountService extends IInterface { public void partitionPublic(String diskId) throws RemoteException; public void partitionPrivate(String diskId) throws RemoteException; public void partitionMixed(String diskId, int ratio) throws RemoteException; + + public void setVolumeNickname(String volId, String nickname) throws RemoteException; + public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException; } diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java index 3965f9d..fd914bc 100644 --- a/core/java/android/os/storage/IMountServiceListener.java +++ b/core/java/android/os/storage/IMountServiceListener.java @@ -91,6 +91,13 @@ public interface IMountServiceListener extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_onVolumeMetadataChanged: { + data.enforceInterface(DESCRIPTOR); + final VolumeInfo vol = (VolumeInfo) data.readParcelable(null); + onVolumeMetadataChanged(vol); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -175,6 +182,22 @@ public interface IMountServiceListener extends IInterface { _data.recycle(); } } + + @Override + public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeParcelable(vol, 0); + mRemote.transact(Stub.TRANSACTION_onVolumeMetadataChanged, _data, _reply, + android.os.IBinder.FLAG_ONEWAY); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } } static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0); @@ -182,6 +205,7 @@ public interface IMountServiceListener extends IInterface { static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2); + static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3); } /** @@ -204,4 +228,6 @@ public interface IMountServiceListener extends IInterface { public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) throws RemoteException; + + public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException; } diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java index 29d5494..28a187d 100644 --- a/core/java/android/os/storage/StorageEventListener.java +++ b/core/java/android/os/storage/StorageEventListener.java @@ -40,4 +40,7 @@ public class StorageEventListener { public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { } + + public void onVolumeMetadataChanged(VolumeInfo vol) { + } } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index bd42f6a..0e977ff 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -35,7 +35,6 @@ import android.util.Log; import android.util.SparseArray; import com.android.internal.os.SomeArgs; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.io.File; @@ -71,6 +70,9 @@ public class StorageManager { /** {@hide} */ public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; + /** {@hide} */ + public static final int FLAG_ALL_METADATA = 1 << 0; + private final Context mContext; private final ContentResolver mResolver; @@ -84,6 +86,7 @@ public class StorageManager { Handler.Callback { private static final int MSG_STORAGE_STATE_CHANGED = 1; private static final int MSG_VOLUME_STATE_CHANGED = 2; + private static final int MSG_VOLUME_METADATA_CHANGED = 3; final StorageEventListener mCallback; final Handler mHandler; @@ -106,6 +109,10 @@ public class StorageManager { mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); args.recycle(); return true; + case MSG_VOLUME_METADATA_CHANGED: + mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1); + args.recycle(); + return true; } args.recycle(); return false; @@ -133,6 +140,13 @@ public class StorageManager { args.argi3 = newState; mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); } + + @Override + public void onVolumeMetadataChanged(VolumeInfo vol) { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = vol; + mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget(); + } } /** @@ -456,18 +470,6 @@ public class StorageManager { } /** {@hide} */ - public @Nullable DiskInfo findDiskByVolumeId(String volId) { - Preconditions.checkNotNull(volId); - // TODO; go directly to service to make this faster - for (DiskInfo disk : getDisks()) { - if (ArrayUtils.contains(disk.volumes, volId)) { - return disk; - } - } - return null; - } - - /** {@hide} */ public @Nullable VolumeInfo findVolumeById(String id) { Preconditions.checkNotNull(id); // TODO; go directly to service to make this faster @@ -493,25 +495,27 @@ public class StorageManager { /** {@hide} */ public @NonNull List<VolumeInfo> getVolumes() { + return getVolumes(0); + } + + /** {@hide} */ + public @NonNull List<VolumeInfo> getVolumes(int flags) { try { - return Arrays.asList(mMountService.getVolumes()); + return Arrays.asList(mMountService.getVolumes(flags)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** {@hide} */ - public @Nullable String getBestVolumeDescription(String volId) { - String descrip = null; - - final VolumeInfo vol = findVolumeById(volId); - if (vol != null) { - descrip = vol.getDescription(); - } + public @Nullable String getBestVolumeDescription(VolumeInfo vol) { + String descrip = vol.getDescription(); - final DiskInfo disk = findDiskByVolumeId(volId); - if (disk != null && TextUtils.isEmpty(descrip)) { - descrip = disk.getDescription(); + if (vol.diskId != null) { + final DiskInfo disk = findDiskById(vol.diskId); + if (disk != null && TextUtils.isEmpty(descrip)) { + descrip = disk.getDescription(); + } } return descrip; @@ -572,6 +576,35 @@ public class StorageManager { } /** {@hide} */ + public void setVolumeNickname(String volId, String nickname) { + try { + mMountService.setVolumeNickname(volId, nickname); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ + public void setVolumeInited(String volId, boolean inited) { + try { + mMountService.setVolumeUserFlags(volId, inited ? VolumeInfo.USER_FLAG_INITED : 0, + VolumeInfo.USER_FLAG_INITED); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ + public void setVolumeSnoozed(String volId, boolean snoozed) { + try { + mMountService.setVolumeUserFlags(volId, snoozed ? VolumeInfo.USER_FLAG_SNOOZED : 0, + VolumeInfo.USER_FLAG_SNOOZED); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ public @Nullable StorageVolume getStorageVolume(File file) { return getStorageVolume(getVolumeList(), file); } diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index beca8b8..f06fc8c 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -22,10 +22,12 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.mtp.MtpStorage; +import android.net.Uri; import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.provider.DocumentsContract; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DebugUtils; @@ -45,6 +47,8 @@ import java.io.File; * @hide */ public class VolumeInfo implements Parcelable { + public static final String EXTRA_VOLUME_ID = "android.os.storage.extra.VOLUME_ID"; + /** Stub volume representing internal private storage */ public static final String ID_PRIVATE_INTERNAL = "private"; /** Real volume representing internal emulated storage */ @@ -67,6 +71,9 @@ public class VolumeInfo implements Parcelable { public static final int FLAG_PRIMARY = 1 << 0; public static final int FLAG_VISIBLE = 1 << 1; + public static final int USER_FLAG_INITED = 1 << 0; + public static final int USER_FLAG_SNOOZED = 1 << 1; + private static SparseArray<String> sStateToEnvironment = new SparseArray<>(); private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>(); @@ -100,7 +107,9 @@ public class VolumeInfo implements Parcelable { /** Framework state */ public final int mtpIndex; + public String diskId; public String nickname; + public int userFlags = 0; public VolumeInfo(String id, int type, int mtpIndex) { this.id = Preconditions.checkNotNull(id); @@ -119,7 +128,9 @@ public class VolumeInfo implements Parcelable { fsLabel = parcel.readString(); path = parcel.readString(); mtpIndex = parcel.readInt(); + diskId = parcel.readString(); nickname = parcel.readString(); + userFlags = parcel.readInt(); } public static @NonNull String getEnvironmentForState(int state) { @@ -139,6 +150,30 @@ public class VolumeInfo implements Parcelable { return getBroadcastForEnvironment(getEnvironmentForState(state)); } + public @NonNull String getId() { + return id; + } + + public @Nullable String getDiskId() { + return diskId; + } + + public int getType() { + return type; + } + + public int getState() { + return state; + } + + public @Nullable String getFsUuid() { + return fsUuid; + } + + public @Nullable String getNickname() { + return nickname; + } + public @Nullable String getDescription() { if (ID_PRIVATE_INTERNAL.equals(id)) { return Resources.getSystem().getString(com.android.internal.R.string.storage_internal); @@ -159,6 +194,14 @@ public class VolumeInfo implements Parcelable { return (flags & FLAG_VISIBLE) != 0; } + public boolean isInited() { + return (userFlags & USER_FLAG_INITED) != 0; + } + + public boolean isSnoozed() { + return (userFlags & USER_FLAG_SNOOZED) != 0; + } + public boolean isVisibleToUser(int userId) { if (type == TYPE_PUBLIC && userId == this.userId) { return isVisible(); @@ -169,6 +212,10 @@ public class VolumeInfo implements Parcelable { } } + public File getPath() { + return new File(path); + } + public File getPathForUser(int userId) { if (path == null) { return null; @@ -228,6 +275,34 @@ public class VolumeInfo implements Parcelable { fsUuid, envState); } + // TODO: avoid this layering violation + private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents"; + private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary"; + + /** + * Build an intent to browse the contents of this volume. Only valid for + * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}. + */ + public Intent buildBrowseIntent() { + final Uri uri; + if (type == VolumeInfo.TYPE_PUBLIC) { + uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid); + } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(id)) { + uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, + DOCUMENT_ROOT_PRIMARY_EMULATED); + } else if (type == VolumeInfo.TYPE_EMULATED) { + // TODO: build intent once supported + uri = null; + } else { + throw new IllegalArgumentException(); + } + + final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(uri); + return intent; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); @@ -250,6 +325,9 @@ public class VolumeInfo implements Parcelable { pw.println(); pw.printPair("path", path); pw.printPair("mtpIndex", mtpIndex); + pw.printPair("diskId", diskId); + pw.printPair("nickname", nickname); + pw.printPair("userFlags", DebugUtils.flagsToString(getClass(), "USER_FLAG_", userFlags)); pw.decreaseIndent(); pw.println(); } @@ -295,6 +373,8 @@ public class VolumeInfo implements Parcelable { parcel.writeString(fsLabel); parcel.writeString(path); parcel.writeInt(mtpIndex); + parcel.writeString(diskId); parcel.writeString(nickname); + parcel.writeInt(userFlags); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 109c23b..3707694 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2766,8 +2766,6 @@ public final class Settings { * It was about AudioManager's setting and thus affected all the applications which * relied on the setting, while this is purely about the vibration setting for incoming * calls. - * - * @hide */ public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing"; @@ -2788,7 +2786,6 @@ public final class Settings { * DTMF tone type played by the dialer when dialing. * 0 = Normal * 1 = Long - * @hide */ public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; @@ -7057,7 +7054,6 @@ public final class Settings { /** * Setting to 1 will hide carrier network settings. * Default is 0. - * @hide */ public static final String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings"; @@ -7724,6 +7720,13 @@ public final class Settings { * @hide */ public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator"; + + /** + * Whether to enable contacts metadata syncing or not + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } /** diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index fa782e4..cc7f880 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -21,6 +21,7 @@ import android.annotation.SdkConstant; import android.app.INotificationManager; import android.app.Notification; import android.app.Notification.Builder; +import android.app.NotificationManager.Policy; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -501,6 +502,22 @@ public abstract class NotificationListenerService extends Service { } /** + * Gets the notification policy token associated with this listener. + * + * <p> + * Returns null if this listener is not currently active. + */ + public final Policy.Token getNotificationPolicyToken() { + if (!isBound()) return null; + try { + return getNotificationInterface().getPolicyTokenFromListener(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + return null; + } + } + + /** * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 1ed4779..14e947c 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -16,6 +16,7 @@ package android.service.notification; +import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; @@ -470,6 +471,59 @@ public class ZenModeConfig implements Parcelable { } }; + public Policy toNotificationPolicy() { + int priorityCategories = 0; + int prioritySenders = Policy.PRIORITY_SENDERS_ANY; + if (allowCalls) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + } + if (allowMessages) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + } + if (allowEvents) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + if (allowReminders) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + } + if (allowRepeatCallers) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + switch (allowFrom) { + case SOURCE_ANYONE: + prioritySenders = Policy.PRIORITY_SENDERS_ANY; + break; + case SOURCE_CONTACT: + prioritySenders = Policy.PRIORITY_SENDERS_CONTACTS; + break; + case SOURCE_STAR: + prioritySenders = Policy.PRIORITY_SENDERS_STARRED; + break; + } + return new Policy(priorityCategories, prioritySenders); + } + + public void applyNotificationPolicy(Policy policy) { + if (policy == null) return; + allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; + allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; + allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; + allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; + allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) + != 0; + switch (policy.prioritySenders) { + case Policy.PRIORITY_SENDERS_CONTACTS: + allowFrom = SOURCE_CONTACT; + break; + case Policy.PRIORITY_SENDERS_STARRED: + allowFrom = SOURCE_STAR; + break; + default: + allowFrom = SOURCE_ANYONE; + break; + } + } + public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; @@ -881,4 +935,5 @@ public class ZenModeConfig implements Parcelable { public interface Migration { ZenModeConfig migrate(XmlV1 v1); } + } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 2bcb352..67794b1 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -165,6 +165,19 @@ public class StaticLayout extends Layout { return this; } + public Builder setIndents(int[] leftIndents, int[] rightIndents) { + int leftLen = leftIndents == null ? 0 : leftIndents.length; + int rightLen = rightIndents == null ? 0 : rightIndents.length; + int[] indents = new int[Math.max(leftLen, rightLen)]; + for (int i = 0; i < indents.length; i++) { + int leftMargin = i < leftLen ? leftIndents[i] : 0; + int rightMargin = i < rightLen ? rightIndents[i] : 0; + indents[i] = leftMargin + rightMargin; + } + nSetIndents(mNativePtr, indents); + return this; + } + /** * Measurement and break iteration is done in native code. The protocol for using * the native code is as follows. @@ -811,7 +824,7 @@ public class StaticLayout extends Layout { float sum = 0; int i; - for (i = len; i >= 0; i--) { + for (i = len; i > 0; i--) { float w = widths[i - 1 + lineStart - widthStart]; if (w + sum + ellipsisWidth > avail) { @@ -1009,6 +1022,8 @@ public class StaticLayout extends Layout { private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); + private static native void nSetIndents(long nativePtr, int[] indents); + // Set up paragraph text and settings; done as one big method to minimize jni crossings private static native void nSetupParagraph(long nativePtr, char[] text, int length, float firstWidth, int firstWidthLineCount, float restWidth, diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java index 5af2832..794c8e7 100644 --- a/core/java/android/view/PhoneWindow.java +++ b/core/java/android/view/PhoneWindow.java @@ -4257,7 +4257,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (deviceId != 0) { searchEvent = new SearchEvent(InputDevice.getDevice(deviceId)); } - result = cb.onSearchRequested(searchEvent); + try { + result = cb.onSearchRequested(searchEvent); + } catch (AbstractMethodError e) { + Log.e(TAG, "WindowCallback " + cb.getClass().getName() + " does not implement" + + " method onSearchRequested(SearchEvent); fa", e); + result = cb.onSearchRequested(); + } } if (!result && (getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 87d5d9a..25c5127 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -360,7 +360,7 @@ public class ThreadedRenderer extends HardwareRenderer { @Override boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { return nCopyLayerInto(mNativeProxy, - layer.getDeferredLayerUpdater(), bitmap.getSkBitmap()); + layer.getDeferredLayerUpdater(), bitmap); } @Override @@ -460,8 +460,6 @@ public class ThreadedRenderer extends HardwareRenderer { if (buffer != null) { long[] map = atlas.getMap(); if (map != null) { - // TODO Remove after fixing b/15425820 - validateMap(context, map); nSetAtlas(renderProxy, buffer, map); } // If IAssetAtlas is not the same class as the IBinder @@ -476,32 +474,6 @@ public class ThreadedRenderer extends HardwareRenderer { Log.w(LOG_TAG, "Could not acquire atlas", e); } } - - private static void validateMap(Context context, long[] map) { - Log.d("Atlas", "Validating map..."); - HashSet<Long> preloadedPointers = new HashSet<Long>(); - - // We only care about drawables that hold bitmaps - final Resources resources = context.getResources(); - final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables(); - - final int count = drawables.size(); - ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>(); - for (int i = 0; i < count; i++) { - drawables.valueAt(i).addAtlasableBitmaps(tmpList); - for (int j = 0; j < tmpList.size(); j++) { - preloadedPointers.add(tmpList.get(j).getSkBitmap()); - } - tmpList.clear(); - } - - for (int i = 0; i < map.length; i += 4) { - if (!preloadedPointers.contains(map[i])) { - Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i])); - map[i] = 0; - } - } - } } static native void setupShadersDiskCache(String cacheFile); @@ -531,7 +503,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native long nCreateTextureLayer(long nativeProxy); private static native void nBuildLayer(long nativeProxy, long node); - private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap); + private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap); private static native void nPushLayerUpdate(long nativeProxy, long layer); private static native void nCancelLayerUpdate(long nativeProxy, long layer); private static native void nDetachSurfaceTexture(long nativeProxy, long layer); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6b28746..25fa349 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15205,27 +15205,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * This method is called by ViewGroup.drawChild() to have each child view draw itself. - * This draw() method is an implementation detail and is not intended to be overridden or - * to be called from anywhere else other than ViewGroup.drawChild(). + * + * This is where the View specializes rendering behavior based on layer type, + * and hardware acceleration. */ boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { - boolean usingRenderNodeProperties = mAttachInfo != null && mAttachInfo.mHardwareAccelerated; + final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated(); + /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList. + * + * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't + * HW accelerated, it can't handle drawing RenderNodes. + */ + boolean drawingWithRenderNode = mAttachInfo != null + && mAttachInfo.mHardwareAccelerated + && hardwareAcceleratedCanvas; + boolean more = false; final boolean childHasIdentityMatrix = hasIdentityMatrix(); - final int flags = parent.mGroupFlags; + final int parentFlags = parent.mGroupFlags; - if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) { + if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) { parent.getChildTransformation().clear(); parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION; } Transformation transformToApply = null; boolean concatMatrix = false; - - boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired; - int layerType = getLayerType(); - final boolean hardwareAccelerated = canvas.isHardwareAccelerated(); - + final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired; final Animation a = getAnimation(); if (a != null) { more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); @@ -15240,8 +15246,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mRenderNode.setAnimationMatrix(null); mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } - if (!usingRenderNodeProperties && - (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { + if (!drawingWithRenderNode + && (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { final Transformation t = parent.getChildTransformation(); final boolean hasTransform = parent.getChildStaticTransformation(this, t); if (hasTransform) { @@ -15259,7 +15265,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags |= PFLAG_DRAWN; if (!concatMatrix && - (flags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS | + (parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS | ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN && canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) && (mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) { @@ -15268,81 +15274,61 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mPrivateFlags2 &= ~PFLAG2_VIEW_QUICK_REJECTED; - if (hardwareAccelerated) { + if (hardwareAcceleratedCanvas) { // Clear INVALIDATED flag to allow invalidation to occur during rendering, but // retain the flag's value temporarily in the mRecreateDisplayList flag - mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) == PFLAG_INVALIDATED; + mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0; mPrivateFlags &= ~PFLAG_INVALIDATED; } RenderNode renderNode = null; Bitmap cache = null; - boolean hasDisplayList = false; - if (!hardwareAccelerated) { - if (layerType != LAYER_TYPE_NONE) { - layerType = LAYER_TYPE_SOFTWARE; - buildDrawingCache(true); - } + int layerType = getLayerType(); + if (layerType == LAYER_TYPE_SOFTWARE + || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) { + layerType = LAYER_TYPE_SOFTWARE; + buildDrawingCache(true); cache = getDrawingCache(true); - } else { - switch (layerType) { - case LAYER_TYPE_SOFTWARE: - if (usingRenderNodeProperties) { - hasDisplayList = canHaveDisplayList(); - } else { - buildDrawingCache(true); - cache = getDrawingCache(true); - } - break; - case LAYER_TYPE_HARDWARE: - if (usingRenderNodeProperties) { - hasDisplayList = canHaveDisplayList(); - } - break; - case LAYER_TYPE_NONE: - // Delay getting the display list until animation-driven alpha values are - // set up and possibly passed on to the view - hasDisplayList = canHaveDisplayList(); - break; - } } - usingRenderNodeProperties &= hasDisplayList; - if (usingRenderNodeProperties) { + + if (drawingWithRenderNode) { + // Delay getting the display list until animation-driven alpha values are + // set up and possibly passed on to the view renderNode = getDisplayList(); if (!renderNode.isValid()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. renderNode = null; - hasDisplayList = false; - usingRenderNodeProperties = false; + drawingWithRenderNode = false; } } int sx = 0; int sy = 0; - if (!hasDisplayList) { + if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; } - final boolean hasNoCache = cache == null || hasDisplayList; - final boolean offsetForScroll = cache == null && !hasDisplayList && - layerType != LAYER_TYPE_HARDWARE; + final boolean hasNoCache = cache == null || drawingWithRenderNode; + final boolean offsetForScroll = cache == null + && !drawingWithRenderNode + && layerType != LAYER_TYPE_HARDWARE; int restoreTo = -1; - if (!usingRenderNodeProperties || transformToApply != null) { + if (!drawingWithRenderNode || transformToApply != null) { restoreTo = canvas.save(); } if (offsetForScroll) { canvas.translate(mLeft - sx, mTop - sy); } else { - if (!usingRenderNodeProperties) { + if (!drawingWithRenderNode) { canvas.translate(mLeft, mTop); } if (scalingRequired) { - if (usingRenderNodeProperties) { + if (drawingWithRenderNode) { // TODO: Might not need this if we put everything inside the DL restoreTo = canvas.save(); } @@ -15352,9 +15338,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - float alpha = usingRenderNodeProperties ? 1 : (getAlpha() * getTransitionAlpha()); - if (transformToApply != null || alpha < 1 || !hasIdentityMatrix() || - (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) { + float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha()); + if (transformToApply != null + || alpha < 1 + || !hasIdentityMatrix() + || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) { if (transformToApply != null || !childHasIdentityMatrix) { int transX = 0; int transY = 0; @@ -15366,7 +15354,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (transformToApply != null) { if (concatMatrix) { - if (usingRenderNodeProperties) { + if (drawingWithRenderNode) { renderNode.setAnimationMatrix(transformToApply.getMatrix()); } else { // Undo the scroll translation, apply the transformation matrix, @@ -15385,7 +15373,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - if (!childHasIdentityMatrix && !usingRenderNodeProperties) { + if (!childHasIdentityMatrix && !drawingWithRenderNode) { canvas.translate(-transX, -transY); canvas.concat(getMatrix()); canvas.translate(transX, transY); @@ -15393,8 +15381,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // Deal with alpha if it is or used to be <1 - if (alpha < 1 || - (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) { + if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) { if (alpha < 1) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA; } else { @@ -15405,17 +15392,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int multipliedAlpha = (int) (255 * alpha); if (!onSetAlpha(multipliedAlpha)) { int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; - if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 || - layerType != LAYER_TYPE_NONE) { + if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 + || layerType != LAYER_TYPE_NONE) { layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; } - if (usingRenderNodeProperties) { + if (drawingWithRenderNode) { renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha()); - } else if (layerType == LAYER_TYPE_NONE) { - final int scrollX = hasDisplayList ? 0 : sx; - final int scrollY = hasDisplayList ? 0 : sy; - canvas.saveLayerAlpha(scrollX, scrollY, - scrollX + (mRight - mLeft), scrollY + (mBottom - mTop), + } else if (layerType == LAYER_TYPE_NONE) { + canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(), multipliedAlpha, layerFlags); } } else { @@ -15429,15 +15413,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_ALPHA_SET; } - if (!usingRenderNodeProperties) { + if (!drawingWithRenderNode) { // apply clips directly, since RenderNode won't do it for this draw - if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN - && cache == null) { + if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 && cache == null) { if (offsetForScroll) { - canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop)); + canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight()); } else { if (!scalingRequired || cache == null) { - canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop); + canvas.clipRect(0, 0, getWidth(), getHeight()); } else { canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight()); } @@ -15450,22 +15433,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - - - if (!usingRenderNodeProperties && hasDisplayList) { - renderNode = getDisplayList(); - if (!renderNode.isValid()) { - // Uncommon, but possible. If a view is removed from the hierarchy during the call - // to getDisplayList(), the display list will be marked invalid and we should not - // try to use it again. - renderNode = null; - hasDisplayList = false; - } - } - if (hasNoCache) { boolean layerRendered = false; - if (layerType == LAYER_TYPE_HARDWARE && !usingRenderNodeProperties) { + if (layerType == LAYER_TYPE_HARDWARE && !drawingWithRenderNode) { final HardwareLayer layer = getHardwareLayer(); if (layer != null && layer.isValid()) { int restoreAlpha = mLayerPaint.getAlpha(); @@ -15474,16 +15444,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLayerPaint.setAlpha(restoreAlpha); layerRendered = true; } else { - final int scrollX = hasDisplayList ? 0 : sx; - final int scrollY = hasDisplayList ? 0 : sy; - canvas.saveLayer(scrollX, scrollY, - scrollX + mRight - mLeft, scrollY + mBottom - mTop, mLayerPaint, - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); + canvas.saveLayer(sx, sy, sx + getWidth(), sy + getHeight(), mLayerPaint); } } if (!layerRendered) { - if (!hasDisplayList) { + if (!drawingWithRenderNode) { // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; @@ -15493,7 +15459,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } else { mPrivateFlags &= ~PFLAG_DIRTY_MASK; - ((DisplayListCanvas) canvas).drawRenderNode(renderNode, flags); + ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags); } } } else if (cache != null) { @@ -15522,13 +15488,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (a != null && !more) { - if (!hardwareAccelerated && !a.getFillAfter()) { + if (!hardwareAcceleratedCanvas && !a.getFillAfter()) { onSetAlpha(255); } parent.finishAnimatingView(this, a); } - if (more && hardwareAccelerated) { + if (more && hardwareAcceleratedCanvas) { if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) { // alpha animations should cause the child to recreate its display list invalidate(true); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 9d0d5ff..49a72ce 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -595,6 +595,8 @@ public abstract class Window { title="Panel"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) { title="SubPanel"; + } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) { + title="AboveSubPanel"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) { title="AtchDlg"; } else { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 54d78f3..e983910 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -164,13 +164,14 @@ public interface WindowManager extends ViewManager { * be used by applications, and a special permission is required * to use them. * </ul> - * + * * @see #TYPE_BASE_APPLICATION * @see #TYPE_APPLICATION * @see #TYPE_APPLICATION_STARTING * @see #TYPE_APPLICATION_PANEL * @see #TYPE_APPLICATION_MEDIA * @see #TYPE_APPLICATION_SUB_PANEL + * @see #TYPE_APPLICATION_ABOVE_SUB_PANEL * @see #TYPE_APPLICATION_ATTACHED_DIALOG * @see #TYPE_STATUS_BAR * @see #TYPE_SEARCH_BAR @@ -193,6 +194,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"), @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"), @ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"), + @ViewDebug.IntToString(from = TYPE_APPLICATION_ABOVE_SUB_PANEL, to = "TYPE_APPLICATION_ABOVE_SUB_PANEL"), @ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG, to = "TYPE_APPLICATION_ATTACHED_DIALOG"), @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA_OVERLAY, to = "TYPE_APPLICATION_MEDIA_OVERLAY"), @ViewDebug.IntToString(from = TYPE_STATUS_BAR, to = "TYPE_STATUS_BAR"), @@ -260,40 +262,40 @@ public interface WindowManager extends ViewManager { * End of types of application windows. */ public static final int LAST_APPLICATION_WINDOW = 99; - + /** * Start of types of sub-windows. The {@link #token} of these windows * must be set to the window they are attached to. These types of * windows are kept next to their attached window in Z-order, and their * coordinate space is relative to their attached window. */ - public static final int FIRST_SUB_WINDOW = 1000; - + public static final int FIRST_SUB_WINDOW = 1000; + /** * Window type: a panel on top of an application window. These windows * appear on top of their attached window. */ - public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; - + public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; + /** * Window type: window for showing media (such as video). These windows * are displayed behind their attached window. */ - public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1; - + public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1; + /** * Window type: a sub-panel on top of an application window. These * windows are displayed on top their attached window and any * {@link #TYPE_APPLICATION_PANEL} panels. */ - public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2; + public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2; /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout * of the window happens as that of a top-level window, <em>not</em> * as a child of its container. */ - public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3; - + public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3; + /** * Window type: window for showing overlays on top of media windows. * These windows are displayed between TYPE_APPLICATION_MEDIA and the @@ -301,19 +303,26 @@ public interface WindowManager extends ViewManager { * is a big ugly hack so: * @hide */ - public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4; - + public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4; + + /** + * Window type: a above sub-panel on top of an application window and it's + * sub-panel windows. These windows are displayed on top of their attached window + * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels. + */ + public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; + /** * End of types of sub-windows. */ - public static final int LAST_SUB_WINDOW = 1999; - + public static final int LAST_SUB_WINDOW = 1999; + /** * Start of system-specific window types. These are not normally * created by applications. */ public static final int FIRST_SYSTEM_WINDOW = 2000; - + /** * Window type: the status bar. There can be only one status bar * window; it is placed at the top of the screen, and all other diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index e7c4328..7ab5aaa 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2427,9 +2427,7 @@ public class WebView extends AbsoluteLayout @Override public void onProvideVirtualAssistStructure(ViewAssistStructure structure) { - super.onProvideVirtualAssistStructure(structure); - // TODO: enable when chromium backend lands. - // mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure); + mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure); } /** @hide */ diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 1be05f3..93ccd1d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -17,6 +17,7 @@ package android.widget; import android.R; +import android.annotation.Nullable; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.ClipData; @@ -1281,74 +1282,79 @@ public class Editor { EXTRACT_UNKNOWN, outText); } - private boolean extractTextInternal(ExtractedTextRequest request, + private boolean extractTextInternal(@Nullable ExtractedTextRequest request, int partialStartOffset, int partialEndOffset, int delta, - ExtractedText outText) { + @Nullable ExtractedText outText) { + if (request == null || outText == null) { + return false; + } + final CharSequence content = mTextView.getText(); - if (content != null) { - if (partialStartOffset != EXTRACT_NOTHING) { - final int N = content.length(); - if (partialStartOffset < 0) { - outText.partialStartOffset = outText.partialEndOffset = -1; - partialStartOffset = 0; - partialEndOffset = N; - } else { - // Now use the delta to determine the actual amount of text - // we need. - partialEndOffset += delta; - // Adjust offsets to ensure we contain full spans. - if (content instanceof Spanned) { - Spanned spanned = (Spanned)content; - Object[] spans = spanned.getSpans(partialStartOffset, - partialEndOffset, ParcelableSpan.class); - int i = spans.length; - while (i > 0) { - i--; - int j = spanned.getSpanStart(spans[i]); - if (j < partialStartOffset) partialStartOffset = j; - j = spanned.getSpanEnd(spans[i]); - if (j > partialEndOffset) partialEndOffset = j; - } - } - outText.partialStartOffset = partialStartOffset; - outText.partialEndOffset = partialEndOffset - delta; + if (content == null) { + return false; + } - if (partialStartOffset > N) { - partialStartOffset = N; - } else if (partialStartOffset < 0) { - partialStartOffset = 0; - } - if (partialEndOffset > N) { - partialEndOffset = N; - } else if (partialEndOffset < 0) { - partialEndOffset = 0; + if (partialStartOffset != EXTRACT_NOTHING) { + final int N = content.length(); + if (partialStartOffset < 0) { + outText.partialStartOffset = outText.partialEndOffset = -1; + partialStartOffset = 0; + partialEndOffset = N; + } else { + // Now use the delta to determine the actual amount of text + // we need. + partialEndOffset += delta; + // Adjust offsets to ensure we contain full spans. + if (content instanceof Spanned) { + Spanned spanned = (Spanned)content; + Object[] spans = spanned.getSpans(partialStartOffset, + partialEndOffset, ParcelableSpan.class); + int i = spans.length; + while (i > 0) { + i--; + int j = spanned.getSpanStart(spans[i]); + if (j < partialStartOffset) partialStartOffset = j; + j = spanned.getSpanEnd(spans[i]); + if (j > partialEndOffset) partialEndOffset = j; } } - if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) { - outText.text = content.subSequence(partialStartOffset, - partialEndOffset); - } else { - outText.text = TextUtils.substring(content, partialStartOffset, - partialEndOffset); + outText.partialStartOffset = partialStartOffset; + outText.partialEndOffset = partialEndOffset - delta; + + if (partialStartOffset > N) { + partialStartOffset = N; + } else if (partialStartOffset < 0) { + partialStartOffset = 0; + } + if (partialEndOffset > N) { + partialEndOffset = N; + } else if (partialEndOffset < 0) { + partialEndOffset = 0; } - } else { - outText.partialStartOffset = 0; - outText.partialEndOffset = 0; - outText.text = ""; - } - outText.flags = 0; - if (MetaKeyKeyListener.getMetaState(content, MetaKeyKeyListener.META_SELECTING) != 0) { - outText.flags |= ExtractedText.FLAG_SELECTING; } - if (mTextView.isSingleLine()) { - outText.flags |= ExtractedText.FLAG_SINGLE_LINE; + if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) { + outText.text = content.subSequence(partialStartOffset, + partialEndOffset); + } else { + outText.text = TextUtils.substring(content, partialStartOffset, + partialEndOffset); } - outText.startOffset = 0; - outText.selectionStart = mTextView.getSelectionStart(); - outText.selectionEnd = mTextView.getSelectionEnd(); - return true; + } else { + outText.partialStartOffset = 0; + outText.partialEndOffset = 0; + outText.text = ""; } - return false; + outText.flags = 0; + if (MetaKeyKeyListener.getMetaState(content, MetaKeyKeyListener.META_SELECTING) != 0) { + outText.flags |= ExtractedText.FLAG_SELECTING; + } + if (mTextView.isSingleLine()) { + outText.flags |= ExtractedText.FLAG_SINGLE_LINE; + } + outText.startOffset = 0; + outText.selectionStart = mTextView.getSelectionStart(); + outText.selectionEnd = mTextView.getSelectionEnd(); + return true; } boolean reportExtractedText() { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 8792323..c5b5c84 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -860,19 +860,22 @@ public class PopupWindow { } /** - * Set the layout type for this window. Should be one of the TYPE constants defined in - * {@link WindowManager.LayoutParams}. + * Set the layout type for this window. This value will be passed through to + * {@link WindowManager.LayoutParams#type} therefore the value should match any value + * {@link WindowManager.LayoutParams#type} accepts. * * @param layoutType Layout type for this window. - * @hide + * + * @see WindowManager.LayoutParams#type */ public void setWindowLayoutType(int layoutType) { mWindowLayoutType = layoutType; } /** - * @return The layout type for this window. - * @hide + * Returns the layout type for this window. + * + * @see #setWindowLayoutType(int) */ public int getWindowLayoutType() { return mWindowLayoutType; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b44a886..0854eef 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -236,6 +236,9 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; * @attr ref android.R.styleable#TextView_elegantTextHeight * @attr ref android.R.styleable#TextView_letterSpacing * @attr ref android.R.styleable#TextView_fontFeatureSettings + * @attr ref android.R.styleable#TextView_breakStrategy + * @attr ref android.R.styleable#TextView_leftIndents + * @attr ref android.R.styleable#TextView_rightIndents */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { @@ -551,6 +554,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mSpacingAdd = 0.0f; private int mBreakStrategy; + private int[] mLeftIndents; + private int[] mRightIndents; private int mMaximum = Integer.MAX_VALUE; private int mMaxMode = LINES; @@ -1146,6 +1151,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_breakStrategy: mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE); + break; + + case com.android.internal.R.styleable.TextView_leftIndents: + TypedArray margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID)); + mLeftIndents = parseDimensionArray(margins); + break; + + case com.android.internal.R.styleable.TextView_rightIndents: + margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID)); + mRightIndents = parseDimensionArray(margins); + break; } } a.recycle(); @@ -1421,6 +1437,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + private int[] parseDimensionArray(TypedArray dimens) { + if (dimens == null) { + return null; + } + int[] result = new int[dimens.length()]; + for (int i = 0; i < result.length; i++) { + result[i] = dimens.getDimensionPixelSize(i, 0); + } + return result; + } + /** * @hide */ @@ -3019,6 +3046,51 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Set indents. Arguments are arrays holding an indent amount, one per line, measured in + * pixels. For lines past the last element in the array, the last element repeats. + * + * @param leftIndents array of indent values for left margin, in pixels + * @param rightIndents array of indent values for right margin, in pixels + * + * @see #getLeftIndents() + * @see #getRightIndents() + * + * @attr ref android.R.styleable#TextView_leftIndents + * @attr ref android.R.styleable#TextView_rightIndents + */ + public void setIndents(@Nullable int[] leftIndents, @Nullable int[] rightIndents) { + mLeftIndents = leftIndents; + mRightIndents = rightIndents; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + + /** + * Get left indents. See {#link setMargins} for more details. + * + * @return left indents + * @see #setIndents(int[], int[]) + * @attr ref android.R.styleable#TextView_leftIndents + */ + public int[] getLeftIndents() { + return mLeftIndents; + } + + /** + * Get right indents. See {#link setMargins} for more details. + * + * @return right indents + * @see #setIndents(int[], int[]) + * @attr ref android.R.styleable#TextView_rightIndents + */ + public int[] getRightIndents() { + return mRightIndents; + } + + /** * Sets font feature settings. The format is the same as the CSS * font-feature-settings attribute: * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings @@ -6564,6 +6636,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setSpacingAdd(mSpacingAdd) .setIncludePad(mIncludePad) .setBreakStrategy(mBreakStrategy); + if (mLeftIndents != null || mRightIndents != null) { + builder.setIndents(mLeftIndents, mRightIndents); + } if (shouldEllipsize) { builder.setEllipsize(mEllipsize) .setEllipsizedWidth(ellipsisWidth) @@ -6652,6 +6727,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setSpacingAdd(mSpacingAdd) .setIncludePad(mIncludePad) .setBreakStrategy(mBreakStrategy); + if (mLeftIndents != null || mRightIndents != null) { + builder.setIndents(mLeftIndents, mRightIndents); + } if (shouldEllipsize) { builder.setEllipsize(effectiveEllipsize) .setEllipsizedWidth(ellipsisWidth) |