diff options
Diffstat (limited to 'core/java')
57 files changed, 1343 insertions, 844 deletions
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 130754e..8947550 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -16,11 +16,14 @@ package android.animation; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Path; import android.graphics.PointF; import android.util.Log; import android.util.Property; +import java.lang.ref.WeakReference; import java.util.ArrayList; /** @@ -41,10 +44,15 @@ import java.util.ArrayList; * */ public final class ObjectAnimator extends ValueAnimator { + private static final String LOG_TAG = "ObjectAnimator"; + private static final boolean DBG = false; - // The target object on which the property exists, set in the constructor - private Object mTarget; + /** + * A weak reference to the target object on which the property exists, set + * in the constructor. We'll cancel the animation if this goes away. + */ + private WeakReference<Object> mTarget; private String mPropertyName; @@ -78,7 +86,7 @@ public final class ObjectAnimator extends ValueAnimator { * * @param propertyName The name of the property being animated. Should not be null. */ - public void setPropertyName(String propertyName) { + public void setPropertyName(@NonNull String propertyName) { // mValues could be null if this is being constructed piecemeal. Just record the // propertyName to be used later when setValues() is called if so. if (mValues != null) { @@ -100,7 +108,7 @@ public final class ObjectAnimator extends ValueAnimator { * * @param property The property being animated. Should not be null. */ - public void setProperty(Property property) { + public void setProperty(@NonNull Property property) { // mValues could be null if this is being constructed piecemeal. Just record the // propertyName to be used later when setValues() is called if so. if (mValues != null) { @@ -134,6 +142,7 @@ public final class ObjectAnimator extends ValueAnimator { * object (if there was just one) or a comma-separated list of all of the * names (if there are more than one).</p> */ + @Nullable public String getPropertyName() { String propertyName = null; if (mPropertyName != null) { @@ -176,7 +185,7 @@ public final class ObjectAnimator extends ValueAnimator { * @param propertyName The name of the property being animated. */ private ObjectAnimator(Object target, String propertyName) { - mTarget = target; + setTarget(target); setPropertyName(propertyName); } @@ -187,7 +196,7 @@ public final class ObjectAnimator extends ValueAnimator { * @param property The property being animated. */ private <T> ObjectAnimator(T target, Property<T, ?> property) { - mTarget = target; + setTarget(target); setProperty(property); } @@ -574,8 +583,9 @@ public final class ObjectAnimator extends ValueAnimator { * @param path The <code>Path</code> to animate values along. * @return An ObjectAnimator object that is set up to animate along <code>path</code>. */ + @NonNull public static ObjectAnimator ofObject(Object target, String propertyName, - TypeConverter<PointF, ?> converter, Path path) { + @Nullable TypeConverter<PointF, ?> converter, Path path) { PropertyValuesHolder pvh = PropertyValuesHolder.ofObject(propertyName, converter, path); return ofPropertyValuesHolder(target, pvh); } @@ -595,6 +605,7 @@ public final class ObjectAnimator extends ValueAnimator { * @param values A set of values that the animation will animate between over time. * @return An ObjectAnimator object that is set up to animate between the given values. */ + @NonNull public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values) { ObjectAnimator anim = new ObjectAnimator(target, property); @@ -622,6 +633,7 @@ public final class ObjectAnimator extends ValueAnimator { * @param values A set of values that the animation will animate between over time. * @return An ObjectAnimator object that is set up to animate between the given values. */ + @NonNull public static <T, V, P> ObjectAnimator ofObject(T target, Property<T, P> property, TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values) { PropertyValuesHolder pvh = PropertyValuesHolder.ofObject(property, converter, evaluator, @@ -644,8 +656,9 @@ public final class ObjectAnimator extends ValueAnimator { * @param path The <code>Path</code> to animate values along. * @return An ObjectAnimator object that is set up to animate along <code>path</code>. */ - public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property, - TypeConverter<PointF, V> converter, Path path) { + @NonNull + public static <T, V> ObjectAnimator ofObject(T target, @NonNull Property<T, V> property, + @Nullable TypeConverter<PointF, V> converter, Path path) { PropertyValuesHolder pvh = PropertyValuesHolder.ofObject(property, converter, path); return ofPropertyValuesHolder(target, pvh); } @@ -667,10 +680,11 @@ public final class ObjectAnimator extends ValueAnimator { * over time. * @return An ObjectAnimator object that is set up to animate between the given values. */ + @NonNull public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) { ObjectAnimator anim = new ObjectAnimator(); - anim.mTarget = target; + anim.setTarget(target); anim.setValues(values); return anim; } @@ -736,10 +750,10 @@ public final class ObjectAnimator extends ValueAnimator { mAutoCancel = cancel; } - private boolean hasSameTargetAndProperties(Animator anim) { + private boolean hasSameTargetAndProperties(@Nullable Animator anim) { if (anim instanceof ObjectAnimator) { PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues(); - if (((ObjectAnimator) anim).getTarget() == mTarget && + if (((ObjectAnimator) anim).getTarget() == getTarget() && mValues.length == theirValues.length) { for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvhMine = mValues[i]; @@ -789,11 +803,11 @@ public final class ObjectAnimator extends ValueAnimator { } } if (DBG) { - Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration()); + Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; - Log.d("ObjectAnimator", " Values[" + i + "]: " + + Log.d(LOG_TAG, " Values[" + i + "]: " + pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " + keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue()); } @@ -818,9 +832,12 @@ public final class ObjectAnimator extends ValueAnimator { if (!mInitialized) { // mValueType may change due to setter/getter setup; do this before calling super.init(), // which uses mValueType to set up the default type evaluator. - int numValues = mValues.length; - for (int i = 0; i < numValues; ++i) { - mValues[i].setupSetterAndGetter(mTarget); + final Object target = getTarget(); + if (target != null) { + final int numValues = mValues.length; + for (int i = 0; i < numValues; ++i) { + mValues[i].setupSetterAndGetter(target); + } } super.initAnimation(); } @@ -836,6 +853,7 @@ public final class ObjectAnimator extends ValueAnimator { * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>. */ @Override + @NonNull public ObjectAnimator setDuration(long duration) { super.setDuration(duration); return this; @@ -847,8 +865,9 @@ public final class ObjectAnimator extends ValueAnimator { * * @return The object being animated */ + @Nullable public Object getTarget() { - return mTarget; + return mTarget == null ? null : mTarget.get(); } /** @@ -857,10 +876,10 @@ public final class ObjectAnimator extends ValueAnimator { * @param target The object being animated */ @Override - public void setTarget(Object target) { - if (mTarget != target) { - final Object oldTarget = mTarget; - mTarget = target; + public void setTarget(@Nullable Object target) { + final Object oldTarget = getTarget(); + if (oldTarget != target) { + mTarget = target == null ? null : new WeakReference<Object>(target); if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) { return; } @@ -872,18 +891,26 @@ public final class ObjectAnimator extends ValueAnimator { @Override public void setupStartValues() { initAnimation(); - int numValues = mValues.length; - for (int i = 0; i < numValues; ++i) { - mValues[i].setupStartValue(mTarget); + + final Object target = getTarget(); + if (target != null) { + final int numValues = mValues.length; + for (int i = 0; i < numValues; ++i) { + mValues[i].setupStartValue(target); + } } } @Override public void setupEndValues() { initAnimation(); - int numValues = mValues.length; - for (int i = 0; i < numValues; ++i) { - mValues[i].setupEndValue(mTarget); + + final Object target = getTarget(); + if (target != null) { + final int numValues = mValues.length; + for (int i = 0; i < numValues; ++i) { + mValues[i].setupEndValue(target); + } } } @@ -901,10 +928,17 @@ public final class ObjectAnimator extends ValueAnimator { */ @Override void animateValue(float fraction) { + final Object target = getTarget(); + if (mTarget != null && target == null) { + // We lost the target reference, cancel and clean up. + cancel(); + return; + } + super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setAnimatedValue(mTarget); + mValues[i].setAnimatedValue(target); } } @@ -915,9 +949,10 @@ public final class ObjectAnimator extends ValueAnimator { } @Override + @NonNull public String toString() { String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " + - mTarget; + getTarget(); if (mValues != null) { for (int i = 0; i < mValues.length; ++i) { returnVal += "\n " + mValues[i].toString(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d9adba3..ea46044 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -98,6 +98,7 @@ import com.android.internal.os.RuntimeInit; import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.util.FastPrintWriter; import com.android.org.conscrypt.OpenSSLSocketImpl; +import com.android.org.conscrypt.TrustedCertificateStore; import com.google.android.collect.Lists; import dalvik.system.VMRuntime; @@ -5049,6 +5050,10 @@ public final class ActivityThread { Security.addProvider(new AndroidKeyStoreProvider()); + // Make sure TrustedCertificateStore looks in the right place for CA certificates + final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); + TrustedCertificateStore.setDefaultUserDirectory(configDir); + Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index b658597..a4384f8 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -251,13 +251,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { if (view == null) { mEpicenterCallback.setEpicenter(null); } else { - int[] loc = new int[2]; - view.getLocationOnScreen(loc); - int left = loc[0] + Math.round(view.getTranslationX()); - int top = loc[1] + Math.round(view.getTranslationY()); - int right = left + view.getWidth(); - int bottom = top + view.getHeight(); - Rect epicenter = new Rect(left, top, right, bottom); + Rect epicenter = new Rect(); + view.getBoundsOnScreen(epicenter); mEpicenterCallback.setEpicenter(epicenter); } } @@ -492,11 +487,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { protected Bundle captureSharedElementState() { Bundle bundle = new Bundle(); - int[] tempLoc = new int[2]; + Rect tempBounds = new Rect(); for (int i = 0; i < mSharedElementNames.size(); i++) { View sharedElement = mSharedElements.get(i); String name = mSharedElementNames.get(i); - captureSharedElementState(sharedElement, name, bundle, tempLoc); + captureSharedElementState(sharedElement, name, bundle, tempBounds); } return bundle; } @@ -509,20 +504,19 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { * @param name The shared element name in the target Activity to apply the placement * information for. * @param transitionArgs Bundle to store shared element placement information. - * @param tempLoc A temporary int[2] for capturing the current location of views. + * @param tempBounds A temporary Rect for capturing the current location of views. */ private static void captureSharedElementState(View view, String name, Bundle transitionArgs, - int[] tempLoc) { + Rect tempBounds) { Bundle sharedElementBundle = new Bundle(); - view.getLocationOnScreen(tempLoc); - float scaleX = view.getScaleX(); - sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]); - int width = Math.round(view.getWidth() * scaleX); + tempBounds.set(0, 0, view.getWidth(), view.getHeight()); + view.getBoundsOnScreen(tempBounds); + sharedElementBundle.putInt(KEY_SCREEN_X, tempBounds.left); + int width = tempBounds.width(); sharedElementBundle.putInt(KEY_WIDTH, width); - float scaleY = view.getScaleY(); - sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]); - int height = Math.round(view.getHeight() * scaleY); + sharedElementBundle.putInt(KEY_SCREEN_Y, tempBounds.top); + int height = tempBounds.height(); sharedElementBundle.putInt(KEY_HEIGHT, height); sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ()); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index e03224c..ad506e4 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -59,9 +59,7 @@ import android.hardware.ConsumerIrManager; import android.hardware.ISerialManager; import android.hardware.SerialManager; import android.hardware.SystemSensorManager; -import android.hardware.hdmi.HdmiCecManager; import android.hardware.hdmi.HdmiControlManager; -import android.hardware.hdmi.IHdmiCecService; import android.hardware.hdmi.IHdmiControlService; import android.hardware.camera2.CameraManager; import android.hardware.display.DisplayManager; @@ -386,12 +384,6 @@ class ContextImpl extends Context { return new BluetoothManager(ctx); }}); - registerService(HDMI_CEC_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { - IBinder b = ServiceManager.getService(HDMI_CEC_SERVICE); - return new HdmiCecManager(IHdmiCecService.Stub.asInterface(b)); - }}); - registerService(HDMI_CONTROL_SERVICE, new StaticServiceFetcher() { public Object createStaticService() { IBinder b = ServiceManager.getService(HDMI_CONTROL_SERVICE); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 72b5cd9..a1cdf59 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -17,6 +17,8 @@ package android.app; import android.annotation.IntDef; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -74,6 +76,15 @@ public class Notification implements Parcelable private static final String TAG = "Notification"; /** + * An activity that provides a user interface for adjusting notification preferences for its + * containing application. Optional but recommended for apps that post + * {@link android.app.Notification Notifications}. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES + = "android.intent.category.NOTIFICATION_PREFERENCES"; + + /** * Use all default values (where applicable). */ public static final int DEFAULT_ALL = ~0; diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 33c3409..261b15d 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -253,6 +253,7 @@ public class SearchManager * for more information on these schemes. */ public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1"; + /** * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, * then all suggestions will be provided in a format that includes space for two small icons, @@ -269,6 +270,24 @@ public class SearchManager * for more information on these schemes. */ public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column, + * then the image will be displayed when forming the suggestion. The suggested dimension for + * the image is 270x400 px for portrait mode and 400x225 px for landscape mode. The data in the + * column must be a resource ID of a drawable, or a URI in one of the following formats: + * + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li> + * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li> + * </ul> + * + * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} + * for more information on these schemes. + */ + public final static String SUGGEST_COLUMN_RESULT_CARD_IMAGE = "suggest_result_card_image"; + /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, this is the action that will be used when @@ -279,6 +298,7 @@ public class SearchManager * it from the cursor. */ public final static String SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action"; + /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, this is the data that will be used when @@ -289,6 +309,7 @@ public class SearchManager * it is more efficient to specify it using XML metadata and omit it from the cursor. */ public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; + /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, this is the data that will be used when @@ -297,6 +318,7 @@ public class SearchManager * an extra under the key {@link #EXTRA_DATA_KEY}. */ public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, then "/" and this value will be appended to the data @@ -304,6 +326,7 @@ public class SearchManager * appropriate base string. */ public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id"; + /** * Column name for suggestions cursor. <i>Required if action is * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i> If this @@ -331,6 +354,89 @@ public class SearchManager "suggest_spinner_while_refreshing"; /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is media type, you + * should provide this column so search app could understand more about your content. The data + * in the column must specify the MIME type of the content. + */ + public final static String SUGGEST_COLUMN_CONTENT_TYPE = "suggest_content_type"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is media type, you + * should provide this column to specify whether your content is live media such as live video + * or live audio. The value in the column is of integer type with value of either 0 indicating + * non-live content or 1 indicating live content. + */ + public final static String SUGGEST_COLUMN_IS_LIVE = "suggest_is_live"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is video, you should + * provide this column to specify the number of vertical lines. The data in the column is of + * integer type. + */ + public final static String SUGGEST_COLUMN_VIDEO_WIDTH = "suggest_video_width"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is video, you should + * provide this column to specify the number of horizontal lines. The data in the column is of + * integer type. + */ + public final static String SUGGEST_COLUMN_VIDEO_HEIGHT = "suggest_video_height"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content contains audio, you + * should provide this column to specify the audio channel configuration. The data in the + * column is string with format like "channels.subchannels" such as "1.0" or "5.1". + */ + public final static String SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG = "suggest_audio_channel_config"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is purchasable, you + * should provide this column to specify the displayable string representation of the purchase + * price of your content including the currency and the amount. If it's free, you should + * provide localized string to specify that it's free. This column can be omitted if the content + * is not applicable to purchase. + */ + public final static String SUGGEST_COLUMN_PURCHASE_PRICE = "suggest_purchase_price"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is rentable, you + * should provide this column to specify the displayable string representation of the rental + * price of your content including the currency and the amount. If it's free, you should + * provide localized string to specify that it's free. This column can be ommitted if the + * content is not applicable to rent. + */ + public final static String SUGGEST_COLUMN_RENTAL_PRICE = "suggest_rental_price"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content has a rating, you + * should provide this column to specify the rating style of your content. The data in the + * column must be one of the constant values specified in {@link android.media.Rating} + */ + public final static String SUGGEST_COLUMN_RATING_STYLE = "suggest_rating_style"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content has a rating, you + * should provide this column to specify the rating score of your content. The data in the + * column is of float type. See {@link android.media.Rating} about valid rating scores for each + * rating style. + */ + public final static String SUGGEST_COLUMN_RATING_SCORE = "suggest_rating_score"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is video or audio and + * has a known production year, you should provide this column to specify the production year + * of your content. The data in the column is of integer type. + */ + public final static String SUGGEST_COLUMN_PRODUCTION_YEAR = "suggest_production_year"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> If your content is video or audio, you + * should provide this column to specify the duration of your content in milliseconds. The data + * in the column is of long type. + */ + public final static String SUGGEST_COLUMN_DURATION = "suggest_duration"; + + /** * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify * additional flags per item. Multiple flags can be specified. * <p> diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java index f42839e..fe29fb7 100644 --- a/core/java/android/app/TaskManagerImpl.java +++ b/core/java/android/app/TaskManagerImpl.java @@ -20,6 +20,7 @@ package android.app; import android.app.task.ITaskManager; import android.app.task.Task; import android.app.task.TaskManager; +import android.os.RemoteException; import java.util.List; @@ -37,26 +38,35 @@ public class TaskManagerImpl extends TaskManager { @Override public int schedule(Task task) { - // TODO Auto-generated method stub - return 0; + try { + return mBinder.schedule(task); + } catch (RemoteException e) { + return TaskManager.RESULT_FAILURE; + } } @Override public void cancel(int taskId) { - // TODO Auto-generated method stub + try { + mBinder.cancel(taskId); + } catch (RemoteException e) {} } @Override public void cancelAll() { - // TODO Auto-generated method stub + try { + mBinder.cancelAll(); + } catch (RemoteException e) {} } @Override public List<Task> getAllPendingTasks() { - // TODO Auto-generated method stub - return null; + try { + return mBinder.getAllPendingTasks(); + } catch (RemoteException e) { + return null; + } } - } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 2cc15e2..1015514 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -16,6 +16,7 @@ package android.app.admin; +import android.accounts.AccountManager; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Service; @@ -165,15 +166,14 @@ public class DeviceAdminReceiver extends BroadcastReceiver { = "android.app.action.ACTION_PASSWORD_EXPIRING"; /** - * Broadcast Action: This broadcast is sent to the newly created profile when - * the provisioning of a managed profile has completed successfully. It is used in both the - * Profile Owner and the Device Owner provisioning. + * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile + * or managed device has completed successfully. * - * <p>The broadcast is limited to the DeviceAdminReceiver component specified in the message - * that started the provisioning. It is also limited to the managed profile. - * - * <p> The intent may contain the extra - * {@link DevicePolicyManager#EXTRA_PROVISIONING_EMAIL_ADDRESS}. + * <p>The broadcast is limited to the profile that will be managed by the application that + * requested provisioning. In the device owner case the profile is the primary user. + * The broadcast will also be limited to the {@link DeviceAdminReceiver} component + * specified in the original intent or NFC bump that started the provisioning process + * (@see DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE). * * <p>Input: Nothing.</p> * <p>Output: Nothing</p> @@ -310,18 +310,23 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** - * Called on the new profile when managed profile provisioning has completed. - * Managed profile provisioning is the process of setting up the device so that it has a - * separate profile which is managed by the mobile device management(mdm) application that - * triggered the provisioning. + * Called when provisioning of a managed profile or managed device has completed successfully. + * + * <p> As a prerequisit for the execution of this callback the (@link DeviceAdminReceiver} has + * to declare an intent filter for {@link #ACTION_PROFILE_PROVISIONING_COMPLETE}. + * Its component must also be specified in the {@link DevicePolicyManager#EXTRA_DEVICE_ADMIN} + * of the {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} intent that started the + * managed provisioning. * - * <p>As part of provisioning a new profile is created, the mdm is moved to the new profile and - * set as the owner of the profile so that it has full control over it. - * This intent is only received by the mdm package that is set as profile owner during - * provisioning. + * <p>When provisioning is complete, the managed profile is hidden until the profile owner + * calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}. Typically a profile + * owner will enable the profile when it has finished any additional setup such as adding an + * account by using the {@link AccountManager} and calling apis to bring the profile into the + * desired state. * - * <p>Provisioning can be triggered via an intent with the action - * android.managedprovisioning.ACTION_PROVISION_MANAGED_PROFILE. + * <p> Note that provisioning completes without waiting for any server interactions, so the + * profile owner needs to wait for data to be available if required (e.g android device ids or + * other data that is set as a result of server interactions). * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 785987f..4aa4294 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.RestrictionsManager; +import android.media.AudioService; import android.net.ProxyInfo; import android.os.Bundle; import android.os.Handler; @@ -99,17 +100,25 @@ public class DevicePolicyManager { /** * Activity action: Starts the provisioning flow which sets up a managed profile. - * This intent will typically be sent by a mobile device management application(mdm). - * Managed profile provisioning creates a profile, moves the mdm to the profile, - * sets the mdm as the profile owner and removes all non required applications from the profile. - * As a profile owner the mdm than has full control over the managed profile. * - * <p>The intent must contain the extras {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and - * {@link #EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME}. + * <p>A managed profile allows data separation for example for the usage of a + * device as a personal and corporate device. The user which provisioning is started from and + * the managed profile share a launcher. + * + * <p>This intent will typically be sent by a mobile device management application (mdm). + * Provisioning adds a managed profile and sets the mdm as the profile owner who has full + * control over the profile + * + * <p>This intent must contain the extras {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} + * {@link #EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME} and {@link #EXTRA_DEVICE_ADMIN}. * * <p> When managed provisioning has completed, an intent of the type * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the - * mdm app on the managed profile. + * managed profile. The intent is sent to the {@link DeviceAdminReceiver} specified in the + * {@link #EXTRA_DEVICE_ADMIN} exclusively. + * + * If provisioning fails, the managedProfile is removed so the device returns to its previous + * state. * * <p>Input: Nothing.</p> * <p>Output: Nothing</p> @@ -135,7 +144,7 @@ public class DevicePolicyManager { * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}. */ public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME - = "deviceAdminPackageName"; + = "android.app.extra.deviceAdminPackageName"; /** * An int extra used to identify that during the current setup process the user has already @@ -155,7 +164,7 @@ public class DevicePolicyManager { * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} */ public static final String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME - = "defaultManagedProfileName"; + = "android.app.extra.defaultManagedProfileName"; /** * A String extra holding the email address of the profile that is created during managed @@ -889,6 +898,9 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to be able to call this * method; if it has not, a security exception will be thrown. * + * <p> Note that setting the password will automatically reset the expiration time for all + * active admins. Active admins do not need to explicitly call this method in that case. + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param timeout The limit (in ms) that a password can remain in effect. A value of 0 * means there is no restriction (unlimited). @@ -1821,6 +1833,23 @@ public class DevicePolicyManager { return isDeviceOwnerApp(packageName); } + /** + * Clears the current device owner. The caller must be the device owner. + * + * This function should be used cautiously as once it is called it cannot + * be undone. The device owner can only be set as a part of device setup + * before setup completes. + */ + public void clearDeviceOwnerApp() { + if (mService != null) { + try { + mService.clearDeviceOwner(mContext.getPackageName()); + } catch (RemoteException re) { + Log.w(TAG, "Failed to clear device owner"); + } + } + } + /** @hide */ public String getDeviceOwner() { if (mService != null) { @@ -2033,6 +2062,8 @@ public class DevicePolicyManager { * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param filter The {@link IntentFilter} the intent has to match to be also resolved in the * other profile + * @param flags {@link DevicePolicyManager#FLAG_MANAGED_CAN_ACCESS_PARENT} and + * {@link DevicePolicyManager#FLAG_PARENT_CAN_ACCESS_MANAGED} are supported. */ public void addCrossProfileIntentFilter(ComponentName admin, IntentFilter filter, int flags) { if (mService != null) { @@ -2376,4 +2407,37 @@ public class DevicePolicyManager { } } } + + /** + * Called by profile or device owners to set the master volume mute on or off. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param on {@code true} to mute master volume, {@code false} to turn mute off. + */ + public void setMasterVolumeMuted(ComponentName admin, boolean on) { + if (mService != null) { + try { + mService.setMasterVolumeMuted(admin, on); + } catch (RemoteException re) { + Log.w(TAG, "Failed to setMasterMute on device policy service"); + } + } + } + + /** + * Called by profile or device owners to check whether the master volume mute is on or off. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return {@code true} if master volume is muted, {@code false} if it's not. + */ + public boolean isMasterVolumeMuted(ComponentName admin) { + if (mService != null) { + try { + return mService.isMasterVolumeMuted(admin); + } catch (RemoteException re) { + Log.w(TAG, "Failed to get isMasterMute on device policy service"); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3d80869..f8df780 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -108,6 +108,7 @@ interface IDevicePolicyManager { boolean isDeviceOwner(String packageName); String getDeviceOwner(); String getDeviceOwnerName(); + void clearDeviceOwner(String packageName); boolean setProfileOwner(String packageName, String ownerName, int userHandle); String getProfileOwner(int userHandle); @@ -146,4 +147,7 @@ interface IDevicePolicyManager { void setGlobalSetting(in ComponentName who, in String setting, in String value); void setSecureSetting(in ComponentName who, in String setting, in String value); + + void setMasterVolumeMuted(in ComponentName admin, boolean on); + boolean isMasterVolumeMuted(in ComponentName admin); } diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/task/Task.java index 87d57fb..0e660b3 100644 --- a/core/java/android/app/task/Task.java +++ b/core/java/android/app/task/Task.java @@ -48,6 +48,11 @@ public class Task implements Parcelable { * @hide */ public static final int DEFAULT_BACKOFF_POLICY = BackoffPolicy.EXPONENTIAL; + /** + * Maximum backoff we allow for a job, in milliseconds. + * @hide + */ + public static final long MAX_BACKOFF_DELAY_MILLIS = 24 * 60 * 60 * 1000; // 24 hours. /** * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1 @@ -185,7 +190,7 @@ public class Task implements Parcelable { private Task(Parcel in) { taskId = in.readInt(); extras = in.readPersistableBundle(); - service = ComponentName.readFromParcel(in); + service = in.readParcelable(null); requireCharging = in.readInt() == 1; requireDeviceIdle = in.readInt() == 1; networkCapabilities = in.readInt(); @@ -201,7 +206,7 @@ public class Task implements Parcelable { private Task(Task.Builder b) { taskId = b.mTaskId; - extras = new PersistableBundle(b.mExtras); + extras = b.mExtras; service = b.mTaskService; requireCharging = b.mRequiresCharging; requireDeviceIdle = b.mRequiresDeviceIdle; @@ -225,7 +230,7 @@ public class Task implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(taskId); out.writePersistableBundle(extras); - ComponentName.writeToParcel(service, out); + out.writeParcelable(service, flags); out.writeInt(requireCharging ? 1 : 0); out.writeInt(requireDeviceIdle ? 1 : 0); out.writeInt(networkCapabilities); diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 7f8d0ab..64d80a0 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -874,6 +874,26 @@ public final class BluetoothDevice implements Parcelable { } /** + * Returns whether there is an open connection to this device. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return True if there is at least one open connection to this device. + * @hide + */ + public boolean isConnected() { + if (sService == null) { + // BT is not enabled, we cannot be connected. + return false; + } + try { + return sService.isConnected(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** * Get the Bluetooth class of the remote device. * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. * diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 07db8cc..a45c6b8 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -58,6 +58,7 @@ interface IBluetooth boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); + boolean isConnected(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); int getRemoteType(in BluetoothDevice device); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a040efb..2897887 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; @@ -2148,8 +2149,6 @@ public abstract class Context { * @see android.app.SearchManager * @see #SENSOR_SERVICE * @see android.hardware.SensorManager - * @see #HDMI_CEC_SERVICE - * @see android.hardware.hdmi.HdmiCecManager * @see #STORAGE_SERVICE * @see android.os.storage.StorageManager * @see #VIBRATOR_SERVICE @@ -2637,23 +2636,14 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@link android.hardware.hdmi.HdmiCecManager} for controlling and managing - * HDMI-CEC protocol. - * - * @see #getSystemService - * @see android.hardware.hdmi.HdmiCecManager - */ - // TODO: Remove this once HdmiControlService is ready. - public static final String HDMI_CEC_SERVICE = "hdmi_cec"; - - /** - * Use with {@link #getSystemService} to retrieve a * {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing * HDMI-CEC protocol. * * @see #getSystemService * @see android.hardware.hdmi.HdmiControlManager + * @hide */ + @SystemApi public static final String HDMI_CONTROL_SERVICE = "hdmi_control"; /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index bd07470..6e53a6f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2656,7 +2656,9 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent to the primary user when an associated managed profile is added (the profile * was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies - * the UserHandle of the profile that was added. This is only sent to registered receivers, + * the UserHandle of the profile that was added. Only applications (for example Launchers) + * that need to display merged content across both primary and managed profiles need to + * worry about this broadcast. This is only sent to registered receivers, * not manifest receivers. */ public static final String ACTION_MANAGED_PROFILE_ADDED = @@ -2664,8 +2666,10 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent to the primary user when an associated managed profile is removed. Carries an - * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed. This - * is only sent to registered receivers, not manifest receivers. + * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed. + * Only applications (for example Launchers) that need to display merged content across both + * primary and managed profiles need to worry about this broadcast. This is only sent to + * registered receivers, not manifest receivers. */ public static final String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED"; @@ -2731,6 +2735,7 @@ public class Intent implements Parcelable, Cloneable { * returned in {@link #getClipData()}. * * @see DocumentsContract + * @see #ACTION_OPEN_DOCUMENT_TREE * @see #ACTION_CREATE_DOCUMENT * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION */ @@ -2765,28 +2770,30 @@ public class Intent implements Parcelable, Cloneable { * * @see DocumentsContract * @see #ACTION_OPEN_DOCUMENT + * @see #ACTION_OPEN_DOCUMENT_TREE * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; /** - * Activity Action: Allow the user to pick a directory. When invoked, the - * system will display the various {@link DocumentsProvider} instances - * installed on the device, letting the user navigate through them. Apps can - * fully manage documents within the returned directory. + * Activity Action: Allow the user to pick a directory subtree. When + * invoked, the system will display the various {@link DocumentsProvider} + * instances installed on the device, letting the user navigate through + * them. Apps can fully manage documents within the returned directory. * <p> * To gain access to descendant (child, grandchild, etc) documents, use - * {@link DocumentsContract#buildDocumentViaUri(Uri, String)} and - * {@link DocumentsContract#buildChildDocumentsViaUri(Uri, String)} using - * the returned directory URI. + * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)} and + * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} + * with the returned URI. * <p> - * Output: The URI representing the selected directory. + * Output: The URI representing the selected directory tree. * * @see DocumentsContract */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY"; + public static final String + ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; // --------------------------------------------------------------------- // --------------------------------------------------------------------- @@ -2976,14 +2983,6 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE"; - /** - * An activity that provides a user interface for adjusting notification preferences for its - * containing application. Optional but recommended for apps that post - * {@link android.app.Notification Notifications}. - */ - @SdkConstant(SdkConstantType.INTENT_CATEGORY) - public static final String CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES"; - // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Application launch intent categories (see addCategory()). @@ -3373,8 +3372,8 @@ public class Intent implements Parcelable, Cloneable { * * @see #ACTION_GET_CONTENT * @see #ACTION_OPEN_DOCUMENT + * @see #ACTION_OPEN_DOCUMENT_TREE * @see #ACTION_CREATE_DOCUMENT - * @see #ACTION_PICK_DIRECTORY */ public static final String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY"; @@ -3712,30 +3711,8 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000; /** - * If set, this marks a point in the task's activity stack that should - * be cleared when the task is reset. That is, the next time the task - * is brought to the foreground with - * {@link #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED} (typically as a result of - * the user re-launching it from home), this activity and all on top of - * it will be finished so that the user does not return to them, but - * instead returns to whatever activity preceeded it. - * - * <p>When this flag is assigned to the root activity all activities up - * to, but not including the root activity, will be cleared. This prevents - * this flag from being used to finish all activities in a task and thereby - * ending the task. - * - * <p>This is useful for cases where you have a logical break in your - * application. For example, an e-mail application may have a command - * to view an attachment, which launches an image view activity to - * display it. This activity should be part of the e-mail application's - * task, since it is a part of the task the user is involved in. However, - * if the user leaves that task, and later selects the e-mail app from - * home, we may like them to return to the conversation they were - * viewing, not the picture attachment, since that is confusing. By - * setting this flag when launching the image viewer, that viewer and - * any activities it starts will be removed the next time the user returns - * to mail. + * @deprecated As of API 21 this performs identically to + * {@link #FLAG_ACTIVITY_NEW_DOCUMENT} which should be used instead of this. */ public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000; /** @@ -3762,8 +3739,7 @@ public class Intent implements Parcelable, Cloneable { * @see android.R.attr#documentLaunchMode * @see #FLAG_ACTIVITY_MULTIPLE_TASK */ - public static final int FLAG_ACTIVITY_NEW_DOCUMENT = - FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | FLAG_ACTIVITY_NEW_TASK; + public static final int FLAG_ACTIVITY_NEW_DOCUMENT = FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET; /** * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint} * callback from occurring on the current frontmost activity before it is diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java index 869f85c..7619c6d 100644 --- a/core/java/android/content/SyncRequest.java +++ b/core/java/android/content/SyncRequest.java @@ -21,6 +21,11 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +/** + * Convenience class to construct sync requests. See {@link android.content.SyncRequest.Builder} + * for an explanation of the various functions. The resulting object is passed through to the + * framework via {@link android.content.ContentResolver#requestSync(SyncRequest)}. + */ public class SyncRequest implements Parcelable { private static final String TAG = "SyncRequest"; /** Account to pass to the sync adapter. Can be null. */ @@ -57,6 +62,10 @@ public class SyncRequest implements Parcelable { return mIsPeriodic; } + /** + * {@hide} + * @return whether this sync is expedited. + */ public boolean isExpedited() { return mIsExpedited; } @@ -199,14 +208,8 @@ public class SyncRequest implements Parcelable { * discriminate between equivalent syncs. */ private Bundle mSyncConfigExtras; - /** Expected upload transfer in bytes. */ - private long mTxBytes = -1L; - /** Expected download transfer in bytes. */ - private long mRxBytes = -1L; /** Whether or not this sync can occur on metered networks. Default false. */ private boolean mDisallowMetered; - /** Priority of this sync relative to others from calling app [-2, 2]. Default 0. */ - private int mPriority = 0; /** * Whether this builder is building a periodic sync, or a one-time sync. */ @@ -314,7 +317,6 @@ public class SyncRequest implements Parcelable { return this; } - /** {@hide} */ private void setupInterval(long at, long before) { if (before > at) { throw new IllegalArgumentException("Specified run time for the sync must be" + @@ -477,18 +479,6 @@ public class SyncRequest implements Parcelable { } /** - * @param priority the priority of this request among all requests from the calling app. - * Range of [-2,2] similar to how this is done with notifications. - */ - public Builder setPriority(int priority) { - if (priority < -2 || priority > 2) { - throw new IllegalArgumentException("Priority must be within range [-2, 2]"); - } - mPriority = priority; - return this; - } - - /** * Performs validation over the request and throws the runtime exception * <code>IllegalArgumentException</code> if this validation fails. * @@ -522,9 +512,6 @@ public class SyncRequest implements Parcelable { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); } - mSyncConfigExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD, mTxBytes); - mSyncConfigExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD, mRxBytes); - mSyncConfigExtras.putInt(ContentResolver.SYNC_EXTRAS_PRIORITY, mPriority); if (mSyncType == SYNC_TYPE_PERIODIC) { // If this is a periodic sync ensure than invalid extras were not set. if (ContentResolver.invalidPeriodicExtras(mCustomExtras) || diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index c2fe3a2..791e5aa 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -84,6 +84,11 @@ public class ActivityInfo extends ComponentInfo */ public static final int DOCUMENT_LAUNCH_ALWAYS = 2; /** + * Constant corresponding to <code>never</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_NEVER = 3; + /** * The document launch mode style requested by the activity. From the * {@link android.R.attr#documentLaunchMode} attribute, one of * {@link #DOCUMENT_LAUNCH_NONE}, {@link #DOCUMENT_LAUNCH_INTO_EXISTING}, @@ -99,6 +104,12 @@ public class ActivityInfo extends ComponentInfo public int documentLaunchMode; /** + * The maximum number of tasks rooted at this activity that can be in the recent task list. + * Refer to {@link android.R.attr#maxRecents}. + */ + public int maxRecents; + + /** * Optional name of a permission required to be able to access this * Activity. From the "permission" attribute. */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ab8bf61..4cac7fd 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2511,6 +2511,9 @@ public class PackageParser { a.info.documentLaunchMode = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE); + a.info.maxRecents = sa.getInt( + com.android.internal.R.styleable.AndroidManifestActivity_maxRecents, + 15); a.info.screenOrientation = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index c3e042e..f829f5e 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -118,9 +118,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { if (configureSuccess) { mStateListener.onConfigured(this); + if (VERBOSE) Log.v(TAG, "ctor - Created session successfully"); } else { mStateListener.onConfigureFailed(this); mClosed = true; // do not fire any other callbacks, do not allow any other work + Log.e(TAG, "Failed to create capture session; configuration failed"); } } @@ -132,6 +134,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { @Override public synchronized int capture(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { + if (request == null) { + throw new IllegalArgumentException("request must not be null"); + } + checkNotClosed(); checkLegalToCapture(); @@ -139,7 +145,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { if (VERBOSE) { Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" + - "" + handler); + " " + handler); } return addPendingSequence(mDeviceImpl.capture(request, @@ -149,6 +155,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { @Override public synchronized int captureBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { + if (requests == null) { + throw new IllegalArgumentException("requests must not be null"); + } else if (requests.isEmpty()) { + throw new IllegalArgumentException("requests must have at least one element"); + } + checkNotClosed(); checkLegalToCapture(); @@ -167,11 +179,20 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { @Override public synchronized int setRepeatingRequest(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { + if (request == null) { + throw new IllegalArgumentException("request must not be null"); + } + checkNotClosed(); checkLegalToCapture(); handler = checkHandler(handler); + if (VERBOSE) { + Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener + + " handler" + " " + handler); + } + return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, createCaptureListenerProxy(handler, listener), mDeviceHandler)); } @@ -179,6 +200,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { @Override public synchronized int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { + if (requests == null) { + throw new IllegalArgumentException("requests must not be null"); + } else if (requests.isEmpty()) { + throw new IllegalArgumentException("requests must have at least one element"); + } + checkNotClosed(); checkLegalToCapture(); @@ -249,8 +276,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * but this would introduce nondeterministic behavior. */ + if (VERBOSE) Log.v(TAG, "replaceSessionClose"); + // #close was already called explicitly, keep going the slow route if (mClosed) { + if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called"); return; } @@ -260,21 +290,39 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { @Override public synchronized void close() { + if (mClosed) { + if (VERBOSE) Log.v(TAG, "close - reentering"); return; } + if (VERBOSE) Log.v(TAG, "close - first time"); + mClosed = true; /* * Flush out any repeating request. Since camera is closed, no new requests * can be queued, and eventually the entire request queue will be drained. * - * Once this is done, wait for camera to idle, then unconfigure the camera. - * Once that's done, fire #onClosed. + * If the camera device was already closed, short circuit and do nothing; since + * no more internal device callbacks will fire anyway. + * + * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the + * camera. Once that's done, fire #onClosed. */ try { mDeviceImpl.stopRepeating(); + } catch (IllegalStateException e) { + // OK: Camera device may already be closed, nothing else to do + Log.w(TAG, "The camera device was already closed: ", e); + + // TODO: Fire onClosed anytime we get the device onClosed or the ISE? + // or just suppress the ISE only and rely onClosed. + // Also skip any of the draining work if this is already closed. + + // Short-circuit; queue listener immediately and return + mStateListener.onClosed(this); + return; } catch (CameraAccessException e) { // OK: close does not throw checked exceptions. Log.e(TAG, "Exception while stopping repeating: ", e); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index d4adae1..d9f3af4 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -257,6 +257,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { synchronized (mLock) { mInError = true; mDeviceHandler.post(new Runnable() { + @Override public void run() { if (isError) { mDeviceListener.onError(CameraDeviceImpl.this, code); @@ -360,6 +361,9 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } catch (CameraAccessException e) { configureSuccess = false; pendingException = e; + if (DEBUG) { + Log.v(TAG, "createCaptureSession - failed with exception ", e); + } } // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java index 3cba9a1..dc09f62 100644 --- a/core/java/android/hardware/camera2/utils/TaskDrainer.java +++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java @@ -52,7 +52,7 @@ public class TaskDrainer<T> { } private static final String TAG = "TaskDrainer"; - private static final boolean VERBOSE = false; + private final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private final Handler mHandler; private final DrainListener mListener; diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index 723eda1..8ad9463 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -16,9 +16,14 @@ package android.hardware.hdmi; +import android.annotation.SystemApi; + /** * Defines constants and utility methods related to HDMI-CEC protocol. + * + * @hide */ +@SystemApi public final class HdmiCec { /** TV device type. */ diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java deleted file mode 100644 index dcb3624..0000000 --- a/core/java/android/hardware/hdmi/HdmiCecClient.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2014 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.hardware.hdmi; - -import android.os.IBinder; -import android.os.RemoteException; - -import android.util.Log; - -/** - * HdmiCecClient is used to control HDMI-CEC logical device instance in the system. - * It is connected to actual hardware part via HdmiCecService. It provides with methods - * to send CEC messages to other device on the bus, and listener that allows to receive - * incoming messages to the device. - */ -public final class HdmiCecClient { - private static final String TAG = "HdmiCecClient"; - - private final IHdmiCecService mService; - private final IBinder mBinder; - - /** - * Listener used by the client to get the incoming messages. - */ - public static abstract class Listener { - /** - * Called when CEC message arrives. Override this method to receive the incoming - * CEC messages from other device on the bus. - * - * @param message {@link HdmiCecMessage} object - */ - public void onMessageReceived(HdmiCecMessage message) { } - - /** - * Called when hotplug event occurs. Override this method to receive the events. - * - * @param connected true if the cable is connected; otherwise false. - */ - public void onCableStatusChanged(boolean connected) { } - } - - // Private constructor. - private HdmiCecClient(IHdmiCecService service, IBinder b) { - mService = service; - mBinder = b; - } - - // Factory method for HdmiCecClient. - // Declared package-private. Accessed by HdmiCecManager only. - static HdmiCecClient create(IHdmiCecService service, IBinder b) { - return new HdmiCecClient(service, b); - } - - /** - * Send <Active Source> message. - */ - public void sendActiveSource() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Inactive Source> message. - */ - public void sendInactiveSource() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Text View On> message. - */ - public void sendTextViewOn() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Image View On> message. - */ - public void sendImageViewOn() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Give Device Power Status> message. - * - * @param address logical address of the device to send the message to, such as - * {@link HdmiCec#ADDR_TV}. - */ - public void sendGiveDevicePowerStatus(int address) { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Returns true if the TV or attached display is powered on. - * <p> - * The result of this method is only meaningful on playback devices (where the device - * type is {@link HdmiCec#DEVICE_PLAYBACK}). - * </p> - * - * @return true if TV is on; otherwise false. - */ - public boolean isTvOn() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - return true; - } -} diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java index 9698445..fbfcca0 100644 --- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java +++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java @@ -16,6 +16,7 @@ package android.hardware.hdmi; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -23,7 +24,10 @@ import android.os.Parcelable; * A class to encapsulate device information for HDMI-CEC. This container * include basic information such as logical address, physical address and * device type, and additional information like vendor id and osd name. + * + * @hide */ +@SystemApi public final class HdmiCecDeviceInfo implements Parcelable { // Logical address, phsical address, device type, vendor id and display name // are immutable value. diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java deleted file mode 100644 index 03c46d8..0000000 --- a/core/java/android/hardware/hdmi/HdmiCecManager.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2014 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.hardware.hdmi; - -import android.os.IBinder; -import android.os.RemoteException; - -/** - * The HdmiCecManager class is used to provide an HdmiCecClient instance, - * get various information on HDMI ports configuration. It is connected to actual hardware - * via HdmiCecService. - */ -public final class HdmiCecManager { - private final IHdmiCecService mService; - - /** - * @hide - hide this constructor because it has a parameter of type IHdmiCecService, - * which is a system private class. The right way to create an instance of this class - * is using the factory Context.getSystemService. - */ - public HdmiCecManager(IHdmiCecService service) { - mService = service; - } - - /** - * Provide the HdmiCecClient instance of the given type. It also registers the listener - * for client to get the events coming to the device. - * - * @param type type of the HDMI-CEC logical device - * @param listener listener to be called - * @return {@link HdmiCecClient} instance. {@code null} on failure. - */ - public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) { - return HdmiCecClient.create(mService, null); - } - - private IHdmiCecListener getListenerWrapper(final HdmiCecClient.Listener listener) { - // TODO: The message/events are not yet forwarded to client since it is not clearly - // defined as to how/who to handle them. Revisit it once the decision is - // made on what messages will have to reach the clients, what will be - // handled by service/manager. - return new IHdmiCecListener.Stub() { - @Override - public void onMessageReceived(HdmiCecMessage message) { - // Do nothing. - } - - @Override - public void onCableStatusChanged(boolean connected) { - // Do nothing. - } - }; - } -} diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java index 62fa279..ac16ad8 100644 --- a/core/java/android/hardware/hdmi/HdmiCecMessage.java +++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java @@ -16,6 +16,7 @@ package android.hardware.hdmi; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -27,7 +28,10 @@ import java.util.Arrays; * A class to encapsulate HDMI-CEC message used for the devices connected via * HDMI cable to communicate with one another. A message is defined by its * source and destination address, command (or opcode), and optional parameters. + * + * @hide */ +@SystemApi public final class HdmiCecMessage implements Parcelable { public static final byte[] EMPTY_PARAM = EmptyArray.BYTE; diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 5b6e862..f15fa00 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -17,6 +17,7 @@ package android.hardware.hdmi; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.RemoteException; /** @@ -28,7 +29,10 @@ import android.os.RemoteException; * {@link HdmiTvClient} object if the system is configured to host one. Android system * can host more than one logical CEC devices. If multiple types are configured they * all work as if they were independent logical devices running in the system. + * + * @hide */ +@SystemApi public final class HdmiControlManager { @Nullable private final IHdmiControlService mService; diff --git a/core/java/android/hardware/hdmi/HdmiHotplugEvent.java b/core/java/android/hardware/hdmi/HdmiHotplugEvent.java index 1462f83..7be4bc5 100644 --- a/core/java/android/hardware/hdmi/HdmiHotplugEvent.java +++ b/core/java/android/hardware/hdmi/HdmiHotplugEvent.java @@ -16,12 +16,16 @@ package android.hardware.hdmi; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; /** * A class that describes the HDMI port hotplug event. + * + * @hide */ +@SystemApi public final class HdmiHotplugEvent implements Parcelable { private final int mPort; diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java index f0bd237..2e49a38 100644 --- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java +++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java @@ -16,6 +16,7 @@ package android.hardware.hdmi; +import android.annotation.SystemApi; import android.os.RemoteException; import android.util.Log; @@ -25,7 +26,10 @@ import android.util.Log; * in the Android system which acts as a playback device such as set-top box. * It provides with methods that control, get information from TV/Display device * connected through HDMI bus. + * + * @hide */ +@SystemApi public final class HdmiPlaybackClient { private static final String TAG = "HdmiPlaybackClient"; diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index 73c7247..6dc4a4f 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -15,11 +15,16 @@ */ package android.hardware.hdmi; +import android.annotation.SystemApi; + /** * HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system * which acts as TV/Display. It provides with methods that manage, interact with other * devices on the CEC bus. + * + * @hide */ +@SystemApi public final class HdmiTvClient { private static final String TAG = "HdmiTvClient"; diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl deleted file mode 100644 index d281ce6..0000000 --- a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2014 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.hardware.hdmi; - -import android.hardware.hdmi.HdmiCecMessage; - -/** - * Interface definition for HdmiCecService to do interprocess communcation. - * - * @hide - */ -oneway interface IHdmiCecListener { - void onMessageReceived(in HdmiCecMessage message); - void onCableStatusChanged(in boolean connected); -} diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl deleted file mode 100644 index ecdd345..0000000 --- a/core/java/android/hardware/hdmi/IHdmiCecService.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2014 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.hardware.hdmi; - -import android.hardware.hdmi.HdmiCecMessage; -import android.hardware.hdmi.IHdmiCecListener; -import android.os.IBinder; - -/** - * Binder interface that components running in the appplication process - * will use to enable HDMI-CEC protocol exchange with other devices. - * - * @hide - */ -interface IHdmiCecService { - IBinder allocateLogicalDevice(int type, IHdmiCecListener listener); - void removeServiceListener(IBinder b, IHdmiCecListener listener); - void sendActiveSource(IBinder b); - void sendInactiveSource(IBinder b); - void sendImageViewOn(IBinder b); - void sendTextViewOn(IBinder b); - void sendGiveDevicePowerStatus(IBinder b, int address); - boolean isTvOn(IBinder b); - void sendMessage(IBinder b, in HdmiCecMessage message); -} - diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index b96f166..27402668 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -22,6 +22,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.net.NetworkUtils; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Handler; @@ -892,6 +893,7 @@ public class ConnectivityManager { case NetworkCapabilities.NET_CAPABILITY_IMS: case NetworkCapabilities.NET_CAPABILITY_RCS: case NetworkCapabilities.NET_CAPABILITY_XCAP: + case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED: //there by default continue; default: // At least one capability usually provided by unrestricted @@ -981,13 +983,13 @@ public class ConnectivityManager { public void onAvailable(NetworkRequest request, Network network) { currentNetwork = network; Log.d(TAG, "startUsingNetworkFeature got Network:" + network); - network.bindProcessForHostResolution(); + setProcessDefaultNetworkForHostResolution(network); } @Override public void onLost(NetworkRequest request, Network network) { if (network.equals(currentNetwork)) { currentNetwork = null; - network.unbindProcessForHostResolution(); + setProcessDefaultNetworkForHostResolution(null); } Log.d(TAG, "startUsingNetworkFeature lost Network:" + network); } @@ -1071,7 +1073,7 @@ public class ConnectivityManager { * @return {@code true} on success, {@code false} on failure * * @deprecated Deprecated in favor of the {@link #requestNetwork}, - * {@link Network#bindProcess} and {@link Network#socketFactory} api. + * {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api. */ public boolean requestRouteToHost(int networkType, int hostAddress) { InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress); @@ -1095,7 +1097,7 @@ public class ConnectivityManager { * @return {@code true} on success, {@code false} on failure * @hide * @deprecated Deprecated in favor of the {@link #requestNetwork} and - * {@link Network#bindProcess} api. + * {@link #setProcessDefaultNetwork} api. */ public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) { byte[] address = hostAddress.getAddress(); @@ -2347,4 +2349,64 @@ public class ConnectivityManager { mService.releaseNetworkRequest(networkRequest); } catch (RemoteException e) {} } + + /** + * Binds the current process to {@code network}. All Sockets created in the future + * (and not explicitly bound via a bound SocketFactory from + * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to + * {@code network}. All host name resolutions will be limited to {@code network} as well. + * Note that if {@code network} ever disconnects, all Sockets created in this way will cease to + * work and all host name resolutions will fail. This is by design so an application doesn't + * accidentally use Sockets it thinks are still bound to a particular {@link Network}. + * To clear binding pass {@code null} for {@code network}. Using individually bound + * Sockets created by Network.getSocketFactory().createSocket() and + * performing network-specific host name resolutions via + * {@link Network#getAllByName Network.getAllByName} is preferred to calling + * {@code setProcessDefaultNetwork}. + * + * @param network The {@link Network} to bind the current process to, or {@code null} to clear + * the current binding. + * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid. + */ + public static boolean setProcessDefaultNetwork(Network network) { + if (network == null) { + NetworkUtils.unbindProcessToNetwork(); + } else { + NetworkUtils.bindProcessToNetwork(network.netId); + } + // TODO fix return value + return true; + } + + /** + * Returns the {@link Network} currently bound to this process via + * {@link #setProcessDefaultNetwork}, or {@code null} if no {@link Network} is explicitly bound. + * + * @return {@code Network} to which this process is bound, or {@code null}. + */ + public static Network getProcessDefaultNetwork() { + int netId = NetworkUtils.getNetworkBoundToProcess(); + if (netId == 0) return null; + return new Network(netId); + } + + /** + * Binds host resolutions performed by this process to {@code network}. + * {@link #setProcessDefaultNetwork} takes precedence over this setting. + * + * @param network The {@link Network} to bind host resolutions from the current process to, or + * {@code null} to clear the current binding. + * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid. + * @hide + * @deprecated This is strictly for legacy usage to support {@link #startUsingNetworkFeature}. + */ + public static boolean setProcessDefaultNetworkForHostResolution(Network network) { + if (network == null) { + NetworkUtils.unbindProcessToNetworkForHostResolution(); + } else { + NetworkUtils.bindProcessToNetworkForHostResolution(network.netId); + } + // TODO hook up the return value. + return true; + } } diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 22b26b1..49a307e 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -74,8 +74,10 @@ public class DhcpResults implements Parcelable { if (linkProperties.getRoutes().size() == 0) { for (RouteInfo r : orig.linkProperties.getRoutes()) linkProperties.addRoute(r); } - if (linkProperties.getDnses().size() == 0) { - for (InetAddress d : orig.linkProperties.getDnses()) linkProperties.addDns(d); + if (linkProperties.getDnsServers().size() == 0) { + for (InetAddress d : orig.linkProperties.getDnsServers()) { + linkProperties.addDnsServer(d); + } } } @@ -211,7 +213,7 @@ public class DhcpResults implements Parcelable { public boolean addDns(String addrString) { if (TextUtils.isEmpty(addrString) == false) { try { - linkProperties.addDns(NetworkUtils.numericToInetAddress(addrString)); + linkProperties.addDnsServer(NetworkUtils.numericToInetAddress(addrString)); } catch (IllegalArgumentException e) { Log.e(TAG, "addDns failed with addrString " + addrString); return true; diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java index 66f0fd0..7acf3f5 100644 --- a/core/java/android/net/DnsPinger.java +++ b/core/java/android/net/DnsPinger.java @@ -248,7 +248,7 @@ public final class DnsPinger extends Handler { return mDefaultDns; } - Collection<InetAddress> dnses = curLinkProps.getDnses(); + Collection<InetAddress> dnses = curLinkProps.getDnsServers(); if (dnses == null || dnses.size() == 0) { loge("getDns::LinkProps has null dns - returning default"); return mDefaultDns; diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 3c36679..cff9025 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Hashtable; +import java.util.List; /** * Describes the properties of a network link. @@ -58,10 +59,12 @@ public class LinkProperties implements Parcelable { private Hashtable<String, LinkProperties> mStackedLinks = new Hashtable<String, LinkProperties>(); - // @hide + /** + * @hide + */ public static class CompareResult<T> { - public Collection<T> removed = new ArrayList<T>(); - public Collection<T> added = new ArrayList<T>(); + public List<T> removed = new ArrayList<T>(); + public List<T> added = new ArrayList<T>(); @Override public String toString() { @@ -81,7 +84,7 @@ public class LinkProperties implements Parcelable { if (source != null) { mIfaceName = source.getInterfaceName(); for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l); - for (InetAddress i : source.getDnses()) mDnses.add(i); + for (InetAddress i : source.getDnsServers()) mDnses.add(i); mDomains = source.getDomains(); for (RouteInfo r : source.getRoutes()) mRoutes.add(r); mHttpProxy = (source.getHttpProxy() == null) ? @@ -98,6 +101,7 @@ public class LinkProperties implements Parcelable { * will have their interface changed to match this new value. * * @param iface The name of the network interface used for this link. + * @hide */ public void setInterfaceName(String iface) { mIfaceName = iface; @@ -117,9 +121,11 @@ public class LinkProperties implements Parcelable { return mIfaceName; } - // @hide - public Collection<String> getAllInterfaceNames() { - Collection interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1); + /** + * @hide + */ + public List<String> getAllInterfaceNames() { + List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1); if (mIfaceName != null) interfaceNames.add(new String(mIfaceName)); for (LinkProperties stacked: mStackedLinks.values()) { interfaceNames.addAll(stacked.getAllInterfaceNames()); @@ -134,23 +140,23 @@ public class LinkProperties implements Parcelable { * prefix lengths for each address. This is a simplified utility alternative to * {@link LinkProperties#getLinkAddresses}. * - * @return An umodifiable {@link Collection} of {@link InetAddress} for this link. + * @return An umodifiable {@link List} of {@link InetAddress} for this link. * @hide */ - public Collection<InetAddress> getAddresses() { - Collection<InetAddress> addresses = new ArrayList<InetAddress>(); + public List<InetAddress> getAddresses() { + List<InetAddress> addresses = new ArrayList<InetAddress>(); for (LinkAddress linkAddress : mLinkAddresses) { addresses.add(linkAddress.getAddress()); } - return Collections.unmodifiableCollection(addresses); + return Collections.unmodifiableList(addresses); } /** * Returns all the addresses on this link and all the links stacked above it. * @hide */ - public Collection<InetAddress> getAllAddresses() { - Collection<InetAddress> addresses = new ArrayList<InetAddress>(); + public List<InetAddress> getAllAddresses() { + List<InetAddress> addresses = new ArrayList<InetAddress>(); for (LinkAddress linkAddress : mLinkAddresses) { addresses.add(linkAddress.getAddress()); } @@ -174,6 +180,7 @@ public class LinkProperties implements Parcelable { * same address/prefix does not already exist. If it does exist it is replaced. * @param address The {@code LinkAddress} to add. * @return true if {@code address} was added or updated, false otherwise. + * @hide */ public boolean addLinkAddress(LinkAddress address) { if (address == null) { @@ -200,6 +207,7 @@ public class LinkProperties implements Parcelable { * * @param toRemove A {@link LinkAddress} specifying the address to remove. * @return true if the address was removed, false if it did not exist. + * @hide */ public boolean removeLinkAddress(LinkAddress toRemove) { int i = findLinkAddressIndex(toRemove); @@ -214,18 +222,18 @@ public class LinkProperties implements Parcelable { * Returns all the {@link LinkAddress} on this link. Typically a link will have * one IPv4 address and one or more IPv6 addresses. * - * @return An unmodifiable {@link Collection} of {@link LinkAddress} for this link. + * @return An unmodifiable {@link List} of {@link LinkAddress} for this link. */ - public Collection<LinkAddress> getLinkAddresses() { - return Collections.unmodifiableCollection(mLinkAddresses); + public List<LinkAddress> getLinkAddresses() { + return Collections.unmodifiableList(mLinkAddresses); } /** * Returns all the addresses on this link and all the links stacked above it. * @hide */ - public Collection<LinkAddress> getAllLinkAddresses() { - Collection<LinkAddress> addresses = new ArrayList<LinkAddress>(); + public List<LinkAddress> getAllLinkAddresses() { + List<LinkAddress> addresses = new ArrayList<LinkAddress>(); addresses.addAll(mLinkAddresses); for (LinkProperties stacked: mStackedLinks.values()) { addresses.addAll(stacked.getAllLinkAddresses()); @@ -239,6 +247,7 @@ public class LinkProperties implements Parcelable { * * @param addresses The {@link Collection} of {@link LinkAddress} to set in this * object. + * @hide */ public void setLinkAddresses(Collection<LinkAddress> addresses) { mLinkAddresses.clear(); @@ -250,20 +259,21 @@ public class LinkProperties implements Parcelable { /** * Adds the given {@link InetAddress} to the list of DNS servers. * - * @param dns The {@link InetAddress} to add to the list of DNS servers. + * @param dnsServer The {@link InetAddress} to add to the list of DNS servers. + * @hide */ - public void addDns(InetAddress dns) { - if (dns != null) mDnses.add(dns); + public void addDnsServer(InetAddress dnsServer) { + if (dnsServer != null) mDnses.add(dnsServer); } /** * Returns all the {@link LinkAddress} for DNS servers on this link. * - * @return An umodifiable {@link Collection} of {@link InetAddress} for DNS servers on + * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on * this link. */ - public Collection<InetAddress> getDnses() { - return Collections.unmodifiableCollection(mDnses); + public List<InetAddress> getDnsServers() { + return Collections.unmodifiableList(mDnses); } /** @@ -271,6 +281,7 @@ public class LinkProperties implements Parcelable { * * @param domains A {@link String} listing in priority order the comma separated * domains to search when resolving host names on this link. + * @hide */ public void setDomains(String domains) { mDomains = domains; @@ -323,6 +334,7 @@ public class LinkProperties implements Parcelable { * proper course is to add either un-named or properly named {@link RouteInfo}. * * @param route A {@link RouteInfo} to add to this object. + * @hide */ public void addRoute(RouteInfo route) { if (route != null) { @@ -339,18 +351,18 @@ public class LinkProperties implements Parcelable { /** * Returns all the {@link RouteInfo} set on this link. * - * @return An unmodifiable {@link Collection} of {@link RouteInfo} for this link. + * @return An unmodifiable {@link List} of {@link RouteInfo} for this link. */ - public Collection<RouteInfo> getRoutes() { - return Collections.unmodifiableCollection(mRoutes); + public List<RouteInfo> getRoutes() { + return Collections.unmodifiableList(mRoutes); } /** * Returns all the routes on this link and all the links stacked above it. * @hide */ - public Collection<RouteInfo> getAllRoutes() { - Collection<RouteInfo> routes = new ArrayList(); + public List<RouteInfo> getAllRoutes() { + List<RouteInfo> routes = new ArrayList(); routes.addAll(mRoutes); for (LinkProperties stacked: mStackedLinks.values()) { routes.addAll(stacked.getAllRoutes()); @@ -364,6 +376,7 @@ public class LinkProperties implements Parcelable { * not enforce it and applications may ignore them. * * @param proxy A {@link ProxyInfo} defining the Http Proxy to use on this link. + * @hide */ public void setHttpProxy(ProxyInfo proxy) { mHttpProxy = proxy; @@ -419,16 +432,17 @@ public class LinkProperties implements Parcelable { * Returns all the links stacked on top of this link. * @hide */ - public Collection<LinkProperties> getStackedLinks() { - Collection<LinkProperties> stacked = new ArrayList<LinkProperties>(); + public List<LinkProperties> getStackedLinks() { + List<LinkProperties> stacked = new ArrayList<LinkProperties>(); for (LinkProperties link : mStackedLinks.values()) { stacked.add(new LinkProperties(link)); } - return Collections.unmodifiableCollection(stacked); + return Collections.unmodifiableList(stacked); } /** * Clears this object to its initial state. + * @hide */ public void clear() { mIfaceName = null; @@ -486,6 +500,7 @@ public class LinkProperties implements Parcelable { * Returns true if this link has an IPv4 address. * * @return {@code true} if there is an IPv4 address, {@code false} otherwise. + * @hide */ public boolean hasIPv4Address() { for (LinkAddress address : mLinkAddresses) { @@ -500,6 +515,7 @@ public class LinkProperties implements Parcelable { * Returns true if this link has an IPv6 address. * * @return {@code true} if there is an IPv6 address, {@code false} otherwise. + * @hide */ public boolean hasIPv6Address() { for (LinkAddress address : mLinkAddresses) { @@ -543,7 +559,7 @@ public class LinkProperties implements Parcelable { * @hide */ public boolean isIdenticalDnses(LinkProperties target) { - Collection<InetAddress> targetDnses = target.getDnses(); + Collection<InetAddress> targetDnses = target.getDnsServers(); String targetDomains = target.getDomains(); if (mDomains == null) { if (targetDomains != null) return false; @@ -696,7 +712,7 @@ public class LinkProperties implements Parcelable { result.removed = new ArrayList<InetAddress>(mDnses); result.added.clear(); if (target != null) { - for (InetAddress newAddress : target.getDnses()) { + for (InetAddress newAddress : target.getDnsServers()) { if (! result.removed.remove(newAddress)) { result.added.add(newAddress); } @@ -831,7 +847,7 @@ public class LinkProperties implements Parcelable { addressCount = in.readInt(); for (int i=0; i<addressCount; i++) { try { - netProp.addDns(InetAddress.getByAddress(in.createByteArray())); + netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray())); } catch (UnknownHostException e) { } } netProp.setDomains(in.readString()); diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 64516e6..d933f26 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -32,7 +32,8 @@ import javax.net.SocketFactory; * {@link ConnectivityManager.NetworkCallbackListener} in response to * {@link ConnectivityManager#requestNetwork} or {@link ConnectivityManager#listenForNetwork}. * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis - * through a targeted {@link SocketFactory} or process-wide via {@link #bindProcess}. + * through a targeted {@link SocketFactory} or process-wide via + * {@link ConnectivityManager#setProcessDefaultNetwork}. */ public class Network implements Parcelable { @@ -160,63 +161,13 @@ public class Network implements Parcelable { * @return a {@link SocketFactory} which produces {@link Socket} instances bound to this * {@code Network}. */ - public SocketFactory socketFactory() { + public SocketFactory getSocketFactory() { if (mNetworkBoundSocketFactory == null) { mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId); } return mNetworkBoundSocketFactory; } - /** - * Binds the current process to this network. All sockets created in the future (and not - * explicitly bound via a bound {@link SocketFactory} (see {@link Network#socketFactory}) - * will be bound to this network. Note that if this {@code Network} ever disconnects - * all sockets created in this way will cease to work. This is by design so an application - * doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}. - */ - public void bindProcess() { - NetworkUtils.bindProcessToNetwork(netId); - } - - /** - * Binds host resolutions performed by this process to this network. {@link #bindProcess} - * takes precedence over this setting. - * - * @hide - * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature(). - */ - public void bindProcessForHostResolution() { - NetworkUtils.bindProcessToNetworkForHostResolution(netId); - } - - /** - * Clears any process specific {@link Network} binding for host resolution. This does - * not clear bindings enacted via {@link #bindProcess}. - * - * @hide - * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature(). - */ - public void unbindProcessForHostResolution() { - NetworkUtils.unbindProcessToNetworkForHostResolution(); - } - - /** - * A static utility method to return any {@code Network} currently bound by this process. - * - * @return {@code Network} to which this process is bound. - */ - public static Network getProcessBoundNetwork() { - return new Network(NetworkUtils.getNetworkBoundToProcess()); - } - - /** - * Clear any process specific {@code Network} binding. This reverts a call to - * {@link Network#bindProcess}. - */ - public static void unbindProcess() { - NetworkUtils.unbindProcessToNetwork(); - } - // 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 7e8b1f1..3d0874b 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -80,6 +80,11 @@ public abstract class NetworkAgent extends Handler { */ public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3; + /* centralize place where base network score, and network score scaling, will be + * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE + */ + public static final int WIFI_BASE_SCORE = 60; + /** * Sent by the NetworkAgent to ConnectivityService to pass the current * network score. diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index edb3286..b02f88e 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -111,7 +111,7 @@ public class NetworkUtils { /** * Binds the current process to the network designated by {@code netId}. All sockets created * in the future (and not explicitly bound via a bound {@link SocketFactory} (see - * {@link Network#socketFactory}) will be bound to this network. Note that if this + * {@link Network#getSocketFactory}) will be bound to this network. Note that if this * {@code Network} ever disconnects all sockets created in this way will cease to work. This * is by design so an application doesn't accidentally use sockets it thinks are still bound to * a particular {@code Network}. diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java index 0340e7e..a578383 100644 --- a/core/java/android/net/ProxyDataTracker.java +++ b/core/java/android/net/ProxyDataTracker.java @@ -108,8 +108,8 @@ public class ProxyDataTracker extends BaseNetworkStateTracker { mNetworkCapabilities = new NetworkCapabilities(); mNetworkInfo.setIsAvailable(true); try { - mLinkProperties.addDns(InetAddress.getByName(DNS1)); - mLinkProperties.addDns(InetAddress.getByName(DNS2)); + mLinkProperties.addDnsServer(InetAddress.getByName(DNS1)); + mLinkProperties.addDnsServer(InetAddress.getByName(DNS2)); mLinkProperties.setInterfaceName(INTERFACE_NAME); } catch (UnknownHostException e) { Log.e(TAG, "Could not add DNS address", e); diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 32050dc..537e993 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -128,29 +128,97 @@ public class BatteryManager { public static final int BATTERY_PLUGGED_ANY = BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS; + /* + * Battery property identifiers. These must match the values in + * frameworks/native/include/batteryservice/BatteryService.h + */ + /** Battery capacity in microampere-hours, as an integer. */ + public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; + + /** + * Instantaneous battery current in microamperes, as an integer. Positive + * values indicate net current entering the battery from a charge source, + * negative values indicate net current discharging from the battery. + */ + public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; + + /** + * Average battery current in microamperes, as an integer. Positive + * values indicate net current entering the battery from a charge source, + * negative values indicate net current discharging from the battery. + * The time period over which the average is computed may depend on the + * fuel gauge hardware and its configuration. + */ + public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; + + /** + * Remaining battery capacity as an integer percentage of total capacity + * (with no fractional part). + */ + public static final int BATTERY_PROPERTY_CAPACITY = 4; + + /** + * Battery remaining energy in nanowatt-hours, as a long integer. + */ + public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; + private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar; /** - * Return the requested battery property. + * Query a battery property from the batteryproperties service. * - * @param id identifier from {@link BatteryProperty} of the requested property - * @return a {@link BatteryProperty} object that returns the property value, or null on error + * Returns the requested value, or Long.MIN_VALUE if property not + * supported on this system or on other error. */ - public BatteryProperty getProperty(int id) throws RemoteException { + private long queryProperty(int id) { + long ret; + if (mBatteryPropertiesRegistrar == null) { IBinder b = ServiceManager.getService("batteryproperties"); mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b); if (mBatteryPropertiesRegistrar == null) - return null; + return Long.MIN_VALUE; } - BatteryProperty prop = new BatteryProperty(); - if ((mBatteryPropertiesRegistrar.getProperty(id, prop) == 0) && - (prop.getLong() != Long.MIN_VALUE)) - return prop; - else - return null; + try { + BatteryProperty prop = new BatteryProperty(); + + if (mBatteryPropertiesRegistrar.getProperty(id, prop) == 0) + ret = prop.getLong(); + else + ret = Long.MIN_VALUE; + } catch (RemoteException e) { + ret = Long.MIN_VALUE; + } + + return ret; + } + + /** + * Return the value of a battery property of integer type. If the + * platform does not provide the property queried, this value will + * be Integer.MIN_VALUE. + * + * @param id identifier of the requested property + * + * @return the property value, or Integer.MIN_VALUE if not supported. + */ + public int getIntProperty(int id) { + return (int)queryProperty(id); + } + + /** + * Return the value of a battery property of long type If the + * platform does not provide the property queried, this value will + * be Long.MIN_VALUE. + * + * @param id identifier of the requested property + * + * @return the property value, or Long.MIN_VALUE if not supported. + */ + public long getLongProperty(int id) { + return queryProperty(id); } } diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java index 27dad4f..84119bd 100644 --- a/core/java/android/os/BatteryProperty.java +++ b/core/java/android/os/BatteryProperty.java @@ -20,44 +20,13 @@ import android.os.Parcelable; /** * Battery properties that may be queried using - * {@link BatteryManager#getProperty * BatteryManager.getProperty()} */ -public class BatteryProperty implements Parcelable { - /* - * Battery property identifiers. These must match the values in - * frameworks/native/include/batteryservice/BatteryService.h - */ - /** Battery capacity in microampere-hours, as an integer. */ - public static final int CHARGE_COUNTER = 1; - - /** - * Instantaneous battery current in microamperes, as an integer. Positive - * values indicate net current entering the battery from a charge source, - * negative values indicate net current discharging from the battery. - */ - public static final int CURRENT_NOW = 2; - - /** - * Average battery current in microamperes, as an integer. Positive - * values indicate net current entering the battery from a charge source, - * negative values indicate net current discharging from the battery. - * The time period over which the average is computed may depend on the - * fuel gauge hardware and its configuration. - */ - public static final int CURRENT_AVERAGE = 3; - - /** - * Remaining battery capacity as an integer percentage of total capacity - * (with no fractional part). - */ - public static final int CAPACITY = 4; - - /** - * Battery remaining energy in nanowatt-hours, as a long integer. - */ - public static final int ENERGY_COUNTER = 5; +/** + * @hide + */ +public class BatteryProperty implements Parcelable { private long mValueLong; /** @@ -68,30 +37,12 @@ public class BatteryProperty implements Parcelable { } /** - * Return the value of a property of integer type previously queried - * via {@link BatteryManager#getProperty - * BatteryManager.getProperty()}. If the platform does - * not provide the property queried, this value will be - * Integer.MIN_VALUE. - * - * @return The queried property value, or Integer.MIN_VALUE if not supported. - */ - public int getInt() { - return (int)mValueLong; - } - - /** - * Return the value of a property of long type previously queried - * via {@link BatteryManager#getProperty - * BatteryManager.getProperty()}. If the platform does - * not provide the property queried, this value will be - * Long.MIN_VALUE. - * - * @return The queried property value, or Long.MIN_VALUE if not supported. + * @hide */ public long getLong() { return mValueLong; } + /* * Parcel read/write code must be kept in sync with * frameworks/native/services/batteryservice/BatteryProperty.cpp diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f7a89ba..91fbb9d 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -481,10 +481,11 @@ public class UserManager { } /** - * @hide * Returns whether the current user has been disallowed from performing certain actions * or setting certain settings. - * @param restrictionKey the string key representing the restriction + * + * @param restrictionKey The string key representing the restriction. + * @return {@code true} if the current user has the given restriction, {@code false} otherwise. */ public boolean hasUserRestriction(String restrictionKey) { return hasUserRestriction(restrictionKey, Process.myUserHandle()); diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index a34d9c3..fa85903 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -25,6 +25,7 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.graphics.BitmapFactory; import android.net.Uri; +import android.os.Build; import android.provider.BrowserContract.Bookmarks; import android.provider.BrowserContract.Combined; import android.provider.BrowserContract.History; @@ -404,12 +405,17 @@ public class Browser { null, null, History.DATE_LAST_VISITED + " ASC"); if (cursor.moveToFirst() && cursor.getCount() >= MAX_HISTORY_COUNT) { - final WebIconDatabase iconDb = WebIconDatabase.getInstance(); + WebIconDatabase iconDb = null; + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + iconDb = WebIconDatabase.getInstance(); + } /* eliminate oldest history items */ for (int i = 0; i < TRUNCATE_N_OLDEST; i++) { cr.delete(ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0)), - null, null); - iconDb.releaseIconForPageUrl(cursor.getString(1)); + null, null); + if (iconDb != null) { + iconDb.releaseIconForPageUrl(cursor.getString(1)); + } if (!cursor.moveToNext()) break; } } @@ -469,11 +475,16 @@ public class Browser { cursor = cr.query(History.CONTENT_URI, new String[] { History.URL }, whereClause, null, null); if (cursor.moveToFirst()) { - final WebIconDatabase iconDb = WebIconDatabase.getInstance(); + WebIconDatabase iconDb = null; + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + iconDb = WebIconDatabase.getInstance(); + } do { - // Delete favicons - // TODO don't release if the URL is bookmarked - iconDb.releaseIconForPageUrl(cursor.getString(0)); + // Delete favicons + // TODO don't release if the URL is bookmarked + if (iconDb != null) { + iconDb.releaseIconForPageUrl(cursor.getString(0)); + } } while (cursor.moveToNext()); cr.delete(History.CONTENT_URI, whereClause, null); @@ -568,7 +579,9 @@ public class Browser { */ public static final void requestAllIcons(ContentResolver cr, String where, WebIconDatabase.IconListener listener) { - WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener); + } } /** diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 6b8e2de..327fe4a 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -60,7 +60,8 @@ import java.util.List; * <p> * All client apps must hold a valid URI permission grant to access documents, * typically issued when a user makes a selection through - * {@link Intent#ACTION_OPEN_DOCUMENT} or {@link Intent#ACTION_CREATE_DOCUMENT}. + * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT}, + * or {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. * * @see DocumentsProvider */ @@ -73,8 +74,8 @@ public final class DocumentsContract { // content://com.example/root/sdcard/search/?query=pony // content://com.example/document/12/ // content://com.example/document/12/children/ - // content://com.example/via/12/document/24/ - // content://com.example/via/12/document/24/children/ + // content://com.example/tree/12/document/24/ + // content://com.example/tree/12/document/24/children/ private DocumentsContract() { } @@ -441,12 +442,13 @@ public final class DocumentsContract { public static final int FLAG_SUPPORTS_SEARCH = 1 << 3; /** - * Flag indicating that this root supports directory selection. + * Flag indicating that this root supports testing parent child + * relationships. * * @see #COLUMN_FLAGS * @see DocumentsProvider#isChildDocument(String, String) */ - public static final int FLAG_SUPPORTS_DIR_SELECTION = 1 << 4; + public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4; /** * Flag indicating that this root is currently empty. This may be used @@ -518,7 +520,7 @@ public final class DocumentsContract { private static final String PATH_DOCUMENT = "document"; private static final String PATH_CHILDREN = "children"; private static final String PATH_SEARCH = "search"; - private static final String PATH_VIA = "via"; + private static final String PATH_TREE = "tree"; private static final String PARAM_QUERY = "query"; private static final String PARAM_MANAGE = "manage"; @@ -564,17 +566,17 @@ public final class DocumentsContract { * Build URI representing access to descendant documents of the given * {@link Document#COLUMN_DOCUMENT_ID}. * - * @see #getViaDocumentId(Uri) + * @see #getTreeDocumentId(Uri) */ - public static Uri buildViaUri(String authority, String documentId) { + public static Uri buildTreeDocumentUri(String authority, String documentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) - .appendPath(PATH_VIA).appendPath(documentId).build(); + .appendPath(PATH_TREE).appendPath(documentId).build(); } /** - * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a - * document provider. When queried, a provider will return a single row with - * columns defined by {@link Document}. + * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in + * a document provider. When queried, a provider will return a single row + * with columns defined by {@link Document}. * * @see DocumentsProvider#queryDocument(String, String[]) * @see #getDocumentId(Uri) @@ -585,42 +587,46 @@ public final class DocumentsContract { } /** - * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a - * document provider. Instead of directly accessing the target document, - * gain access via another document. The target document must be a - * descendant (child, grandchild, etc) of the via document. + * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in + * a document provider. When queried, a provider will return a single row + * with columns defined by {@link Document}. + * <p> + * However, instead of directly accessing the target document, the returned + * URI will leverage access granted through a subtree URI, typically + * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target document + * must be a descendant (child, grandchild, etc) of the subtree. * <p> * This is typically used to access documents under a user-selected - * directory, since it doesn't require the user to separately confirm each - * new document access. + * directory tree, since it doesn't require the user to separately confirm + * each new document access. * - * @param viaUri a related document (directory) that the caller is - * leveraging to gain access to the target document. The target - * document must be a descendant of this directory. + * @param treeUri the subtree to leverage to gain access to the target + * document. The target directory must be a descendant of this + * subtree. * @param documentId the target document, which the caller may not have * direct access to. - * @see Intent#ACTION_PICK_DIRECTORY + * @see Intent#ACTION_OPEN_DOCUMENT_TREE * @see DocumentsProvider#isChildDocument(String, String) * @see #buildDocumentUri(String, String) */ - public static Uri buildDocumentViaUri(Uri viaUri, String documentId) { + public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(viaUri.getAuthority()).appendPath(PATH_VIA) - .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT) + .authority(treeUri.getAuthority()).appendPath(PATH_TREE) + .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT) .appendPath(documentId).build(); } /** {@hide} */ - public static Uri buildDocumentMaybeViaUri(Uri baseUri, String documentId) { - if (isViaUri(baseUri)) { - return buildDocumentViaUri(baseUri, documentId); + public static Uri buildDocumentUriMaybeUsingTree(Uri baseUri, String documentId) { + if (isTreeUri(baseUri)) { + return buildDocumentUriUsingTree(baseUri, documentId); } else { return buildDocumentUri(baseUri.getAuthority(), documentId); } } /** - * Build URI representing the children of the given directory in a document + * Build URI representing the children of the target directory in a document * provider. When queried, a provider will return zero or more rows with * columns defined by {@link Document}. * @@ -637,28 +643,33 @@ public final class DocumentsContract { } /** - * Build URI representing the children of the given directory in a document - * provider. Instead of directly accessing the target document, gain access - * via another document. The target document must be a descendant (child, - * grandchild, etc) of the via document. + * Build URI representing the children of the target directory in a document + * provider. When queried, a provider will return zero or more rows with + * columns defined by {@link Document}. + * <p> + * However, instead of directly accessing the target directory, the returned + * URI will leverage access granted through a subtree URI, typically + * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target + * directory must be a descendant (child, grandchild, etc) of the subtree. * <p> * This is typically used to access documents under a user-selected - * directory, since it doesn't require the user to separately confirm each - * new document access. + * directory tree, since it doesn't require the user to separately confirm + * each new document access. * - * @param viaUri a related document (directory) that the caller is - * leveraging to gain access to the target document. The target - * document must be a descendant of this directory. - * @param parentDocumentId the target document, which the caller may not - * have direct access to. - * @see Intent#ACTION_PICK_DIRECTORY + * @param treeUri the subtree to leverage to gain access to the target + * document. The target directory must be a descendant of this + * subtree. + * @param parentDocumentId the document to return children for, which the + * caller may not have direct access to, and which must be a + * directory with MIME type of {@link Document#MIME_TYPE_DIR}. + * @see Intent#ACTION_OPEN_DOCUMENT_TREE * @see DocumentsProvider#isChildDocument(String, String) * @see #buildChildDocumentsUri(String, String) */ - public static Uri buildChildDocumentsViaUri(Uri viaUri, String parentDocumentId) { + public static Uri buildChildDocumentsUriUsingTree(Uri treeUri, String parentDocumentId) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) - .authority(viaUri.getAuthority()).appendPath(PATH_VIA) - .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT) + .authority(treeUri.getAuthority()).appendPath(PATH_TREE) + .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT) .appendPath(parentDocumentId).appendPath(PATH_CHILDREN).build(); } @@ -683,21 +694,24 @@ public final class DocumentsContract { * {@link DocumentsProvider}. * * @see #buildDocumentUri(String, String) - * @see #buildDocumentViaUri(Uri, String) + * @see #buildDocumentUriUsingTree(Uri, String) */ public static boolean isDocumentUri(Context context, Uri uri) { final List<String> paths = uri.getPathSegments(); - if (paths.size() >= 2 - && (PATH_DOCUMENT.equals(paths.get(0)) || PATH_VIA.equals(paths.get(0)))) { + if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) { + return isDocumentsProvider(context, uri.getAuthority()); + } + if (paths.size() == 4 && PATH_TREE.equals(paths.get(0)) + && PATH_DOCUMENT.equals(paths.get(2))) { return isDocumentsProvider(context, uri.getAuthority()); } return false; } /** {@hide} */ - public static boolean isViaUri(Uri uri) { + public static boolean isTreeUri(Uri uri) { final List<String> paths = uri.getPathSegments(); - return (paths.size() >= 2 && PATH_VIA.equals(paths.get(0))); + return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))); } private static boolean isDocumentsProvider(Context context, String authority) { @@ -733,7 +747,7 @@ public final class DocumentsContract { if (paths.size() >= 2 && PATH_DOCUMENT.equals(paths.get(0))) { return paths.get(1); } - if (paths.size() >= 4 && PATH_VIA.equals(paths.get(0)) + if (paths.size() >= 4 && PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2))) { return paths.get(3); } @@ -742,12 +756,10 @@ public final class DocumentsContract { /** * Extract the via {@link Document#COLUMN_DOCUMENT_ID} from the given URI. - * - * @see #isViaUri(Uri) */ - public static String getViaDocumentId(Uri documentUri) { + public static String getTreeDocumentId(Uri documentUri) { final List<String> paths = documentUri.getPathSegments(); - if (paths.size() >= 2 && PATH_VIA.equals(paths.get(0))) { + if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) { return paths.get(1); } throw new IllegalArgumentException("Invalid URI: " + documentUri); diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 066b4aa..021fff4 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -20,10 +20,14 @@ import static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE; import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT; +import static android.provider.DocumentsContract.buildDocumentUri; +import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree; +import static android.provider.DocumentsContract.buildTreeDocumentUri; import static android.provider.DocumentsContract.getDocumentId; import static android.provider.DocumentsContract.getRootId; import static android.provider.DocumentsContract.getSearchDocumentsQuery; -import static android.provider.DocumentsContract.isViaUri; +import static android.provider.DocumentsContract.getTreeDocumentId; +import static android.provider.DocumentsContract.isTreeUri; import android.content.ContentProvider; import android.content.ContentResolver; @@ -117,6 +121,7 @@ import java.util.Objects; * </p> * * @see Intent#ACTION_OPEN_DOCUMENT + * @see Intent#ACTION_OPEN_DOCUMENT_TREE * @see Intent#ACTION_CREATE_DOCUMENT */ public abstract class DocumentsProvider extends ContentProvider { @@ -128,8 +133,8 @@ public abstract class DocumentsProvider extends ContentProvider { private static final int MATCH_SEARCH = 4; private static final int MATCH_DOCUMENT = 5; private static final int MATCH_CHILDREN = 6; - private static final int MATCH_DOCUMENT_VIA = 7; - private static final int MATCH_CHILDREN_VIA = 8; + private static final int MATCH_DOCUMENT_TREE = 7; + private static final int MATCH_CHILDREN_TREE = 8; private String mAuthority; @@ -149,8 +154,8 @@ public abstract class DocumentsProvider extends ContentProvider { mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH); mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT); mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN); - mMatcher.addURI(mAuthority, "via/*/document/*", MATCH_DOCUMENT_VIA); - mMatcher.addURI(mAuthority, "via/*/document/*/children", MATCH_CHILDREN_VIA); + mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE); + mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE); // Sanity check our setup if (!info.exported) { @@ -169,23 +174,24 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Test if a document is descendant (child, grandchild, etc) from the given - * parent. Providers must override this to support directory selection. You - * should avoid making network requests to keep this request fast. + * parent. For example, providers must implement this to support + * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. You should avoid making network + * requests to keep this request fast. * * @param parentDocumentId parent to verify against. * @param documentId child to verify. * @return if given document is a descendant of the given parent. - * @see DocumentsContract.Root#FLAG_SUPPORTS_DIR_SELECTION + * @see DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD */ public boolean isChildDocument(String parentDocumentId, String documentId) { return false; } /** {@hide} */ - private void enforceVia(Uri documentUri) { - if (DocumentsContract.isViaUri(documentUri)) { - final String parent = DocumentsContract.getViaDocumentId(documentUri); - final String child = DocumentsContract.getDocumentId(documentUri); + private void enforceTree(Uri documentUri) { + if (isTreeUri(documentUri)) { + final String parent = getTreeDocumentId(documentUri); + final String child = getDocumentId(documentUri); if (Objects.equals(parent, child)) { return; } @@ -479,12 +485,12 @@ public abstract class DocumentsProvider extends ContentProvider { return querySearchDocuments( getRootId(uri), getSearchDocumentsQuery(uri), projection); case MATCH_DOCUMENT: - case MATCH_DOCUMENT_VIA: - enforceVia(uri); + case MATCH_DOCUMENT_TREE: + enforceTree(uri); return queryDocument(getDocumentId(uri), projection); case MATCH_CHILDREN: - case MATCH_CHILDREN_VIA: - enforceVia(uri); + case MATCH_CHILDREN_TREE: + enforceTree(uri); if (DocumentsContract.isManageMode(uri)) { return queryChildDocumentsForManage( getDocumentId(uri), projection, sortOrder); @@ -512,8 +518,8 @@ public abstract class DocumentsProvider extends ContentProvider { case MATCH_ROOT: return DocumentsContract.Root.MIME_TYPE_ITEM; case MATCH_DOCUMENT: - case MATCH_DOCUMENT_VIA: - enforceVia(uri); + case MATCH_DOCUMENT_TREE: + enforceTree(uri); return getDocumentType(getDocumentId(uri)); default: return null; @@ -530,21 +536,20 @@ public abstract class DocumentsProvider extends ContentProvider { * call the superclass. If the superclass returns {@code null}, the subclass * may implement custom behavior. * <p> - * This is typically used to resolve a "via" URI into a concrete document + * This is typically used to resolve a subtree URI into a concrete document * reference, issuing a narrower single-document URI permission grant along * the way. * - * @see DocumentsContract#buildDocumentViaUri(Uri, String) + * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String) */ @Override public Uri canonicalize(Uri uri) { final Context context = getContext(); switch (mMatcher.match(uri)) { - case MATCH_DOCUMENT_VIA: - enforceVia(uri); + case MATCH_DOCUMENT_TREE: + enforceTree(uri); - final Uri narrowUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), - DocumentsContract.getDocumentId(uri)); + final Uri narrowUri = buildDocumentUri(uri.getAuthority(), getDocumentId(uri)); // Caller may only have prefix grant, so extend them a grant to // the narrow URI. @@ -628,7 +633,7 @@ public abstract class DocumentsProvider extends ContentProvider { throw new SecurityException( "Requested authority " + authority + " doesn't match provider " + mAuthority); } - enforceVia(documentUri); + enforceTree(documentUri); final Bundle out = new Bundle(); try { @@ -641,8 +646,8 @@ public abstract class DocumentsProvider extends ContentProvider { // No need to issue new grants here, since caller either has // manage permission or a prefix grant. We might generate a - // "via" style URI if that's how they called us. - final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(documentUri, + // tree style URI if that's how they called us. + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, newDocumentId); out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); @@ -653,12 +658,12 @@ public abstract class DocumentsProvider extends ContentProvider { final String newDocumentId = renameDocument(documentId, displayName); if (newDocumentId != null) { - final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri( - documentUri, newDocumentId); + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, + newDocumentId); // If caller came in with a narrow grant, issue them a // narrow grant for the newly renamed document. - if (!isViaUri(newDocumentUri)) { + if (!isTreeUri(newDocumentUri)) { final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, documentUri); context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); @@ -694,8 +699,8 @@ public abstract class DocumentsProvider extends ContentProvider { */ public final void revokeDocumentPermission(String documentId) { final Context context = getContext(); - context.revokeUriPermission(DocumentsContract.buildDocumentUri(mAuthority, documentId), ~0); - context.revokeUriPermission(DocumentsContract.buildViaUri(mAuthority, documentId), ~0); + context.revokeUriPermission(buildDocumentUri(mAuthority, documentId), ~0); + context.revokeUriPermission(buildTreeDocumentUri(mAuthority, documentId), ~0); } /** @@ -705,7 +710,7 @@ public abstract class DocumentsProvider extends ContentProvider { */ @Override public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); return openDocument(getDocumentId(uri), mode, null); } @@ -717,7 +722,7 @@ public abstract class DocumentsProvider extends ContentProvider { @Override public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); return openDocument(getDocumentId(uri), mode, signal); } @@ -730,7 +735,7 @@ public abstract class DocumentsProvider extends ContentProvider { @SuppressWarnings("resource") public final AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, null); return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; } @@ -744,7 +749,7 @@ public abstract class DocumentsProvider extends ContentProvider { @SuppressWarnings("resource") public final AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, signal); return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; } @@ -757,7 +762,7 @@ public abstract class DocumentsProvider extends ContentProvider { @Override public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); return openDocumentThumbnail(getDocumentId(uri), sizeHint, null); @@ -775,7 +780,7 @@ public abstract class DocumentsProvider extends ContentProvider { public final AssetFileDescriptor openTypedAssetFile( Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal) throws FileNotFoundException { - enforceVia(uri); + enforceTree(uri); if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) { final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE); return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal); diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index d7e8cf0..3907e77 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -97,6 +97,27 @@ public final class Range<T extends Comparable<? super T>> { } /** + * Checks if the {@code value} is within the bounds of this range. + * + * <p>A value is considered to be within this range if it's {@code >=} then + * the lower endpoint <i>and</i> {@code <=} to the upper endpoint (using the {@link Comparable} + * interface.</p> + * + * @param value a non-{@code null} {@code T} reference + * @return {@code true} if the value is within this inclusive range, {@code false} otherwise + * + * @throws NullPointerException if {@code value} was {@code null} + */ + public boolean inRange(T value) { + checkNotNull(value, "value must not be null"); + + boolean gteLower = value.compareTo(mLower) >= 0; + boolean lteUpper = value.compareTo(mUpper) <= 0; + + return gteLower && lteUpper; + } + + /** * Compare two ranges for equality. * * <p>A range is considered equal if and only if both the lower and upper endpoints @@ -105,16 +126,13 @@ public final class Range<T extends Comparable<? super T>> { * @return {@code true} if the ranges are equal, {@code false} otherwise */ @Override - public boolean equals(final Object obj) { + public boolean equals(Object obj) { if (obj == null) { return false; - } - if (this == obj) { + } else if (this == obj) { return true; - } - if (obj instanceof Range) { + } else if (obj instanceof Range) { @SuppressWarnings("rawtypes") - final Range other = (Range) obj; return mLower.equals(other.mLower) && mUpper.equals(other.mUpper); } diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java index 8d4c67f..9952859 100644 --- a/core/java/android/util/Rational.java +++ b/core/java/android/util/Rational.java @@ -15,29 +15,88 @@ */ package android.util; +import static com.android.internal.util.Preconditions.*; + +import java.io.IOException; +import java.io.InvalidObjectException; + /** * <p>An immutable data type representation a rational number.</p> * * <p>Contains a pair of {@code int}s representing the numerator and denominator of a * Rational number. </p> */ -public final class Rational { +public final class Rational extends Number implements Comparable<Rational> { + /** + * Constant for the <em>Not-a-Number (NaN)</em> value of the {@code Rational} type. + * + * <p>A {@code NaN} value is considered to be equal to itself (that is {@code NaN.equals(NaN)} + * will return {@code true}; it is always greater than any non-{@code NaN} value (that is + * {@code NaN.compareTo(notNaN)} will return a number greater than {@code 0}).</p> + * + * <p>Equivalent to constructing a new rational with both the numerator and denominator + * equal to {@code 0}.</p> + */ + public static final Rational NaN = new Rational(0, 0); + + /** + * Constant for the positive infinity value of the {@code Rational} type. + * + * <p>Equivalent to constructing a new rational with a positive numerator and a denominator + * equal to {@code 0}.</p> + */ + public static final Rational POSITIVE_INFINITY = new Rational(1, 0); + + /** + * Constant for the negative infinity value of the {@code Rational} type. + * + * <p>Equivalent to constructing a new rational with a negative numerator and a denominator + * equal to {@code 0}.</p> + */ + public static final Rational NEGATIVE_INFINITY = new Rational(-1, 0); + + /** + * Constant for the zero value of the {@code Rational} type. + * + * <p>Equivalent to constructing a new rational with a numerator equal to {@code 0} and + * any non-zero denominator.</p> + */ + public static final Rational ZERO = new Rational(0, 1); + + /** + * Unique version number per class to be compliant with {@link java.io.Serializable}. + * + * <p>Increment each time the fields change in any way.</p> + */ + private static final long serialVersionUID = 1L; + + /* + * Do not change the order of these fields or add new instance fields to maintain the + * Serializable compatibility across API revisions. + */ private final int mNumerator; private final int mDenominator; /** - * <p>Create a Rational with a given numerator and denominator.</p> + * <p>Create a {@code Rational} with a given numerator and denominator.</p> * * <p>The signs of the numerator and the denominator may be flipped such that the denominator - * is always positive.</p> + * is always positive. Both the numerator and denominator will be converted to their reduced + * forms (see {@link #equals} for more details).</p> * - * <p>A rational value with a 0-denominator may be constructed, but will have similar semantics - * as float {@code NaN} and {@code INF} values. For {@code NaN}, - * both {@link #getNumerator} and {@link #getDenominator} functions will return 0. For - * positive or negative {@code INF}, only the {@link #getDenominator} will return 0.</p> + * <p>For example, + * <ul> + * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}. + * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1} + * <li>a rational of {@code 5/0} will be reduced to {@code 1/0} + * <li>a rational of {@code 0/5} will be reduced to {@code 0/1} + * </ul> + * </p> * * @param numerator the numerator of the rational * @param denominator the denominator of the rational + * + * @see #equals */ public Rational(int numerator, int denominator) { @@ -46,32 +105,100 @@ public final class Rational { denominator = -denominator; } - mNumerator = numerator; - mDenominator = denominator; + // Convert to reduced form + if (denominator == 0 && numerator > 0) { + mNumerator = 1; // +Inf + mDenominator = 0; + } else if (denominator == 0 && numerator < 0) { + mNumerator = -1; // -Inf + mDenominator = 0; + } else if (denominator == 0 && numerator == 0) { + mNumerator = 0; // NaN + mDenominator = 0; + } else if (numerator == 0) { + mNumerator = 0; + mDenominator = 1; + } else { + int gcd = gcd(numerator, denominator); + + mNumerator = numerator / gcd; + mDenominator = denominator / gcd; + } } /** * Gets the numerator of the rational. + * + * <p>The numerator will always return {@code 1} if this rational represents + * infinity (that is, the denominator is {@code 0}).</p> */ public int getNumerator() { - if (mDenominator == 0) { - return 0; - } return mNumerator; } /** * Gets the denominator of the rational + * + * <p>The denominator may return {@code 0}, in which case the rational may represent + * positive infinity (if the numerator was positive), negative infinity (if the numerator + * was negative), or {@code NaN} (if the numerator was {@code 0}).</p> + * + * <p>The denominator will always return {@code 1} if the numerator is {@code 0}. */ public int getDenominator() { return mDenominator; } - private boolean isNaN() { + /** + * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value. + * + * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p> + * + * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value; + * {@code false} if this is a (potentially infinite) number value + */ + public boolean isNaN() { return mDenominator == 0 && mNumerator == 0; } - private boolean isInf() { + /** + * Indicates whether this rational represents an infinite value. + * + * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p> + * + * @return {@code true} if this rational is a (positive or negative) infinite value; + * {@code false} if this is a finite number value (or {@code NaN}) + */ + public boolean isInfinite() { + return mNumerator != 0 && mDenominator == 0; + } + + /** + * Indicates whether this rational represents a finite value. + * + * <p>A finite value occurs when the denominator is not {@code 0}; in other words + * the rational is neither infinity or {@code NaN}.</p> + * + * @return {@code true} if this rational is a (positive or negative) infinite value; + * {@code false} if this is a finite number value (or {@code NaN}) + */ + public boolean isFinite() { + return mDenominator != 0; + } + + /** + * Indicates whether this rational represents a zero value. + * + * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p> + * + * @return {@code true} if this rational is finite zero value; + * {@code false} otherwise + */ + public boolean isZero() { + return isFinite() && mNumerator == 0; + } + + private boolean isPosInf() { return mDenominator == 0 && mNumerator > 0; } @@ -82,12 +209,12 @@ public final class Rational { /** * <p>Compare this Rational to another object and see if they are equal.</p> * - * <p>A Rational object can only be equal to another Rational object (comparing against any other - * type will return false).</p> + * <p>A Rational object can only be equal to another Rational object (comparing against any + * other type will return {@code false}).</p> * * <p>A Rational object is considered equal to another Rational object if and only if one of - * the following holds</p>: - * <ul><li>Both are NaN</li> + * the following holds:</p> + * <ul><li>Both are {@code NaN}</li> * <li>Both are infinities of the same sign</li> * <li>Both have the same numerator and denominator in their reduced form</li> * </ul> @@ -96,12 +223,12 @@ public final class Rational { * denominator by their greatest common divisor.</p> * * <pre>{@code - * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true - * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false - * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction - * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN) - * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity - * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity + * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true + * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false + * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction + * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN) + * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity + * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity * }</pre> * * @param obj a reference to another object @@ -110,41 +237,31 @@ public final class Rational { */ @Override public boolean equals(Object obj) { - if (obj == null) { - return false; - } else if (obj instanceof Rational) { - Rational other = (Rational) obj; - if (mDenominator == 0 || other.mDenominator == 0) { - if (isNaN() && other.isNaN()) { - return true; - } else if (isInf() && other.isInf() || isNegInf() && other.isNegInf()) { - return true; - } else { - return false; - } - } else if (mNumerator == other.mNumerator && mDenominator == other.mDenominator) { - return true; - } else { - int thisGcd = gcd(); - int otherGcd = other.gcd(); - - int thisNumerator = mNumerator / thisGcd; - int thisDenominator = mDenominator / thisGcd; - - int otherNumerator = other.mNumerator / otherGcd; - int otherDenominator = other.mDenominator / otherGcd; - - return (thisNumerator == otherNumerator && thisDenominator == otherDenominator); - } - } - return false; + return obj instanceof Rational && equals((Rational) obj); + } + + private boolean equals(Rational other) { + return (mNumerator == other.mNumerator && mDenominator == other.mDenominator); } + /** + * Return a string representation of this rational, e.g. {@code "1/2"}. + * + * <p>The following rules of conversion apply: + * <ul> + * <li>{@code NaN} values will return {@code "NaN"} + * <li>Positive infinity values will return {@code "Infinity"} + * <li>Negative infinity values will return {@code "-Infinity"} + * <li>All other values will return {@code "numerator/denominator"} where {@code numerator} + * and {@code denominator} are substituted with the appropriate numerator and denominator + * values. + * </ul></p> + */ @Override public String toString() { if (isNaN()) { return "NaN"; - } else if (isInf()) { + } else if (isPosInf()) { return "Infinity"; } else if (isNegInf()) { return "-Infinity"; @@ -160,7 +277,8 @@ public final class Rational { * @hide */ public float toFloat() { - return (float) mNumerator / (float) mDenominator; + // TODO: remove this duplicate function (used in CTS and the shim) + return floatValue(); } /** @@ -177,20 +295,24 @@ public final class Rational { /** * Calculates the greatest common divisor using Euclid's algorithm. * + * <p><em>Visible for testing only.</em></p> + * + * @param numerator the numerator in a fraction + * @param denominator the denominator in a fraction + * * @return An int value representing the gcd. Always positive. * @hide */ - public int gcd() { - /** + public static int gcd(int numerator, int denominator) { + /* * Non-recursive implementation of Euclid's algorithm: * * gcd(a, 0) := a * gcd(a, b) := gcd(b, a mod b) * */ - - int a = mNumerator; - int b = mDenominator; + int a = numerator; + int b = denominator; while (b != 0) { int oldB = b; @@ -201,4 +323,221 @@ public final class Rational { return Math.abs(a); } + + /** + * Returns the value of the specified number as a {@code double}. + * + * <p>The {@code double} is calculated by converting both the numerator and denominator + * to a {@code double}; then returning the result of dividing the numerator by the + * denominator.</p> + * + * @return the divided value of the numerator and denominator as a {@code double}. + */ + @Override + public double doubleValue() { + double num = mNumerator; + double den = mDenominator; + + return num / den; + } + + /** + * Returns the value of the specified number as a {@code float}. + * + * <p>The {@code float} is calculated by converting both the numerator and denominator + * to a {@code float}; then returning the result of dividing the numerator by the + * denominator.</p> + * + * @return the divided value of the numerator and denominator as a {@code float}. + */ + @Override + public float floatValue() { + float num = mNumerator; + float den = mDenominator; + + return num / den; + } + + /** + * Returns the value of the specified number as a {@code int}. + * + * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value + * by dividing the numerator by the denominator; conversion for non-finite values happens + * identically to casting a floating point value to an {@code int}, in particular: + * + * <p> + * <ul> + * <li>Positive infinity saturates to the largest maximum integer + * {@link Integer#MAX_VALUE}</li> + * <li>Negative infinity saturates to the smallest maximum integer + * {@link Integer#MIN_VALUE}</li> + * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li> + * </ul> + * </p> + * + * @return the divided value of the numerator and denominator as a {@code int}. + */ + @Override + public int intValue() { + // Mimic float to int conversion rules from JLS 5.1.3 + + if (isPosInf()) { + return Integer.MAX_VALUE; + } else if (isNegInf()) { + return Integer.MIN_VALUE; + } else if (isNaN()) { + return 0; + } else { // finite + return mNumerator / mDenominator; + } + } + + /** + * Returns the value of the specified number as a {@code long}. + * + * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value + * by dividing the numerator by the denominator; conversion for non-finite values happens + * identically to casting a floating point value to a {@code long}, in particular: + * + * <p> + * <ul> + * <li>Positive infinity saturates to the largest maximum long + * {@link Long#MAX_VALUE}</li> + * <li>Negative infinity saturates to the smallest maximum long + * {@link Long#MIN_VALUE}</li> + * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li> + * </ul> + * </p> + * + * @return the divided value of the numerator and denominator as a {@code long}. + */ + @Override + public long longValue() { + // Mimic float to long conversion rules from JLS 5.1.3 + + if (isPosInf()) { + return Long.MAX_VALUE; + } else if (isNegInf()) { + return Long.MIN_VALUE; + } else if (isNaN()) { + return 0; + } else { // finite + return mNumerator / mDenominator; + } + } + + /** + * Returns the value of the specified number as a {@code short}. + * + * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value + * identically to {@link #intValue}; the {@code int} result is then truncated to a + * {@code short} before returning the value.</p> + * + * @return the divided value of the numerator and denominator as a {@code short}. + */ + @Override + public short shortValue() { + return (short) intValue(); + } + + /** + * Compare this rational to the specified rational to determine their natural order. + * + * <p>{@link #NaN} is considered to be equal to itself and greater than all other + * {@code Rational} values. Otherwise, if the objects are not {@link #equals equal}, then + * the following rules apply:</p> + * + * <ul> + * <li>Positive infinity is greater than any other finite number (or negative infinity) + * <li>Negative infinity is less than any other finite number (or positive infinity) + * <li>The finite number represented by this rational is checked numerically + * against the other finite number by converting both rationals to a common denominator multiple + * and comparing their numerators. + * </ul> + * + * @param another the rational to be compared + * + * @return a negative integer, zero, or a positive integer as this object is less than, + * equal to, or greater than the specified rational. + * + * @throws NullPointerException if {@code another} was {@code null} + */ + @Override + public int compareTo(Rational another) { + checkNotNull(another, "another must not be null"); + + if (equals(another)) { + return 0; + } else if (isNaN()) { // NaN is greater than the other non-NaN value + return 1; + } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value + return -1; + } else if (isPosInf() || another.isNegInf()) { + return 1; // positive infinity is greater than any non-NaN/non-posInf value + } else if (isNegInf() || another.isPosInf()) { + return -1; // negative infinity is less than any non-NaN/non-negInf value + } + + // else both this and another are finite numbers + + // make the denominators the same, then compare numerators + long thisNumerator = ((long)mNumerator) * another.mDenominator; // long to avoid overflow + long otherNumerator = ((long)another.mNumerator) * mDenominator; // long to avoid overflow + + // avoid underflow from subtraction by doing comparisons + if (thisNumerator < otherNumerator) { + return -1; + } else if (thisNumerator > otherNumerator) { + return 1; + } else { + // This should be covered by #equals, but have this code path just in case + return 0; + } + } + + /* + * Serializable implementation. + * + * The following methods are omitted: + * >> writeObject - the default is sufficient (field by field serialization) + * >> readObjectNoData - the default is sufficient (0s for both fields is a NaN) + */ + + /** + * writeObject with default serialized form - guards against + * deserializing non-reduced forms of the rational. + * + * @throws InvalidObjectException if the invariants were violated + */ + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + + /* + * Guard against trying to deserialize illegal values (in this case, ones + * that don't have a standard reduced form). + * + * - Non-finite values must be one of [0, 1], [0, 0], [0, 1], [0, -1] + * - Finite values must always have their greatest common divisor as 1 + */ + + if (mNumerator == 0) { // either zero or NaN + if (mDenominator == 1 || mDenominator == 0) { + return; + } + throw new InvalidObjectException( + "Rational must be deserialized from a reduced form for zero values"); + } else if (mDenominator == 0) { // either positive or negative infinity + if (mNumerator == 1 || mNumerator == -1) { + return; + } + throw new InvalidObjectException( + "Rational must be deserialized from a reduced form for infinity values"); + } else { // finite value + if (gcd(mNumerator, mDenominator) > 1) { + throw new InvalidObjectException( + "Rational must be deserialized from a reduced form for finite values"); + } + } + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c681919..65b1f8c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -431,7 +431,7 @@ import java.util.concurrent.atomic.AtomicInteger; * child. The child must use this size, and guarantee that all of its * descendants will fit within this size. * <li>AT_MOST: This is used by the parent to impose a maximum size on the - * child. The child must gurantee that it and all of its descendants will fit + * child. The child must guarantee that it and all of its descendants will fit * within this size. * </ul> * </p> @@ -5377,8 +5377,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Gets the location of this view in screen coordintates. * * @param outRect The output location + * @hide */ - void getBoundsOnScreen(Rect outRect) { + public void getBoundsOnScreen(Rect outRect) { if (mAttachInfo == null) { return; } @@ -9670,7 +9671,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * The transform matrix of this view, which is calculated based on the current - * roation, scale, and pivot properties. + * rotation, scale, and pivot properties. * * @see #getRotation() * @see #getScaleX() diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 02011e0..2905851 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -6939,13 +6939,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (getClass() != another.getClass()) { return 1; } - // First is above second. - if (mLocation.bottom - another.mLocation.top <= 0) { - return -1; - } - // First is below second. - if (mLocation.top - another.mLocation.bottom >= 0) { - return 1; + final int topDiference = mLocation.top - another.mLocation.top; + if (topDiference != 0) { + return topDiference; } // LTR if (mLayoutDirection == LAYOUT_DIRECTION_LTR) { @@ -6961,11 +6957,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return -rightDifference; } } - // Break tie by top. - final int topDiference = mLocation.top - another.mLocation.top; - if (topDiference != 0) { - return topDiference; - } // Break tie by height. final int heightDiference = mLocation.height() - another.mLocation.height(); if (heightDiference != 0) { diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 294f472..57e774e 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -87,7 +87,12 @@ public final class WindowInsets { if (mTempRect == null) { mTempRect = new Rect(); } - mTempRect.set(mSystemWindowInsets); + if (mSystemWindowInsets != null) { + mTempRect.set(mSystemWindowInsets); + } else { + // If there were no system window insets, this is just empty. + mTempRect.setEmpty(); + } return mTempRect; } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index a74e3a0..e1f40b7 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -195,6 +195,7 @@ public class BaseInputConnection implements InputConnection { public boolean commitText(CharSequence text, int newCursorPosition) { if (DEBUG) Log.v(TAG, "commitText " + text); replaceText(text, newCursorPosition, false); + mIMM.notifyUserAction(); sendCurrentText(); return true; } @@ -435,6 +436,7 @@ public class BaseInputConnection implements InputConnection { public boolean setComposingText(CharSequence text, int newCursorPosition) { if (DEBUG) Log.v(TAG, "setComposingText " + text); replaceText(text, newCursorPosition, true); + mIMM.notifyUserAction(); return true; } @@ -518,6 +520,7 @@ public class BaseInputConnection implements InputConnection { viewRootImpl.dispatchKeyFromIme(event); } } + mIMM.notifyUserAction(); return false; } @@ -601,9 +604,6 @@ public class BaseInputConnection implements InputConnection { } beginBatchEdit(); - if (!composing && !TextUtils.isEmpty(text)) { - mIMM.notifyUserAction(); - } // delete composing text set previously. int a = getComposingSpanStart(content); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0693617..ace8808 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -320,6 +320,25 @@ public final class InputMethodManager { int mCursorCandEnd; /** + * Represents an invalid action notification sequence number. {@link InputMethodManagerService} + * always issues a positive integer for action notification sequence numbers. Thus -1 is + * guaranteed to be different from any valid sequence number. + */ + private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1; + /** + * The next sequence number that is to be sent to {@link InputMethodManagerService} via + * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed. + */ + private int mNextUserActionNotificationSequenceNumber = + NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER; + + /** + * The last sequence number that is already sent to {@link InputMethodManagerService}. + */ + private int mLastSentUserActionNotificationSequenceNumber = + NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER; + + /** * The instance that has previously been sent to the input method. */ private CursorAnchorInfo mCursorAnchorInfo = null; @@ -364,6 +383,7 @@ public final class InputMethodManager { static final int MSG_TIMEOUT_INPUT_EVENT = 6; static final int MSG_FLUSH_INPUT_EVENT = 7; static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 8; + static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9; class H extends Handler { H(Looper looper) { @@ -503,6 +523,11 @@ public final class InputMethodManager { } return; } + case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: { + synchronized (mH) { + mNextUserActionNotificationSequenceNumber = msg.arg1; + } + } } } } @@ -572,6 +597,12 @@ public final class InputMethodManager { public void setCursorAnchorMonitorMode(int monitorMode) { mH.sendMessage(mH.obtainMessage(MSG_SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, 0)); } + + @Override + public void setUserActionNotificationSequenceNumber(int sequenceNumber) { + mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, + sequenceNumber, 0)); + } }; final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); @@ -1214,6 +1245,8 @@ public final class InputMethodManager { mBindSequence = res.sequence; mCurMethod = res.method; mCurId = res.id; + mNextUserActionNotificationSequenceNumber = + res.userActionNotificationSequenceNumber; } else { if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); @@ -1918,8 +1951,28 @@ public final class InputMethodManager { */ public void notifyUserAction() { synchronized (mH) { + if (mLastSentUserActionNotificationSequenceNumber == + mNextUserActionNotificationSequenceNumber) { + if (DEBUG) { + Log.w(TAG, "Ignoring notifyUserAction as it has already been sent." + + " mLastSentUserActionNotificationSequenceNumber: " + + mLastSentUserActionNotificationSequenceNumber + + " mNextUserActionNotificationSequenceNumber: " + + mNextUserActionNotificationSequenceNumber); + } + return; + } try { - mService.notifyUserAction(); + if (DEBUG) { + Log.w(TAG, "notifyUserAction: " + + " mLastSentUserActionNotificationSequenceNumber: " + + mLastSentUserActionNotificationSequenceNumber + + " mNextUserActionNotificationSequenceNumber: " + + mNextUserActionNotificationSequenceNumber); + } + mService.notifyUserAction(mNextUserActionNotificationSequenceNumber); + mLastSentUserActionNotificationSequenceNumber = + mNextUserActionNotificationSequenceNumber; } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } @@ -2103,6 +2156,10 @@ public final class InputMethodManager { + " mCursorSelEnd=" + mCursorSelEnd + " mCursorCandStart=" + mCursorCandStart + " mCursorCandEnd=" + mCursorCandEnd); + p.println(" mNextUserActionNotificationSequenceNumber=" + + mNextUserActionNotificationSequenceNumber + + " mLastSentUserActionNotificationSequenceNumber=" + + mLastSentUserActionNotificationSequenceNumber); } /** diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 9e8d12b..b100d27 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -28,4 +28,5 @@ oneway interface IInputMethodClient { void onUnbindMethod(int sequence); void setActive(boolean active); void setCursorAnchorMonitorMode(int monitorMode); + void setUserActionNotificationSequenceNumber(int sequenceNumber); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 4590520..b84c359 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -77,6 +77,6 @@ interface IInputMethodManager { boolean setInputMethodEnabled(String id, boolean enabled); void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); int getInputMethodWindowVisibleHeight(); - oneway void notifyUserAction(); + oneway void notifyUserAction(int sequenceNumber); void setCursorAnchorMonitorMode(in IBinder token, int monitorMode); } diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 14afe21..3a3e56d 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -47,13 +47,19 @@ public final class InputBindResult implements Parcelable { * Sequence number of this binding. */ public final int sequence; - + + /** + * Sequence number of user action notification. + */ + public final int userActionNotificationSequenceNumber; + public InputBindResult(IInputMethodSession _method, InputChannel _channel, - String _id, int _sequence) { + String _id, int _sequence, int _userActionNotificationSequenceNumber) { method = _method; channel = _channel; id = _id; sequence = _sequence; + userActionNotificationSequenceNumber = _userActionNotificationSequenceNumber; } InputBindResult(Parcel source) { @@ -65,12 +71,15 @@ public final class InputBindResult implements Parcelable { } id = source.readString(); sequence = source.readInt(); + userActionNotificationSequenceNumber = source.readInt(); } @Override public String toString() { return "InputBindResult{" + method + " " + id - + " #" + sequence + "}"; + + " sequence:" + sequence + + " userActionNotificationSequenceNumber:" + userActionNotificationSequenceNumber + + "}"; } /** @@ -90,6 +99,7 @@ public final class InputBindResult implements Parcelable { } dest.writeString(id); dest.writeInt(sequence); + dest.writeInt(userActionNotificationSequenceNumber); } /** diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java index bf36bb1..43a05d0 100644 --- a/core/java/com/android/server/SystemService.java +++ b/core/java/com/android/server/SystemService.java @@ -193,58 +193,4 @@ public abstract class SystemService { private SystemServiceManager getManager() { return LocalServices.getService(SystemServiceManager.class); } - -// /** -// * Called when a new user has been created. If your service deals with multiple users, this -// * method should be overridden. -// * -// * @param userHandle The user that was created. -// */ -// public void onUserCreated(int userHandle) { -// } -// -// /** -// * Called when an existing user has started a new session. If your service deals with multiple -// * users, this method should be overridden. -// * -// * @param userHandle The user who started a new session. -// */ -// public void onUserStarted(int userHandle) { -// } -// -// /** -// * Called when a background user session has entered the foreground. If your service deals with -// * multiple users, this method should be overridden. -// * -// * @param userHandle The user who's session entered the foreground. -// */ -// public void onUserForeground(int userHandle) { -// } -// -// /** -// * Called when a foreground user session has entered the background. If your service deals with -// * multiple users, this method should be overridden; -// * -// * @param userHandle The user who's session entered the background. -// */ -// public void onUserBackground(int userHandle) { -// } -// -// /** -// * Called when a user's active session has stopped. If your service deals with multiple users, -// * this method should be overridden. -// * -// * @param userHandle The user who's session has stopped. -// */ -// public void onUserStopped(int userHandle) { -// } -// -// /** -// * Called when a user has been removed from the system. If your service deals with multiple -// * users, this method should be overridden. -// * -// * @param userHandle The user who has been removed. -// */ -// public void onUserRemoved(int userHandle) { -// } } |
