summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/animation/Animator.java118
-rw-r--r--core/java/android/animation/AnimatorInflater.java13
-rw-r--r--core/java/android/animation/AnimatorSet.java1
-rw-r--r--core/java/android/animation/ValueAnimator.java11
-rw-r--r--core/java/android/app/Activity.java71
-rw-r--r--core/java/android/app/ActivityThread.java91
-rw-r--r--core/java/android/app/ApplicationPackageManager.java20
-rw-r--r--core/java/android/app/BackStackRecord.java1
-rw-r--r--core/java/android/app/Dialog.java22
-rw-r--r--core/java/android/app/IUserSwitchObserver.aidl1
-rw-r--r--core/java/android/app/Instrumentation.java9
-rw-r--r--core/java/android/app/LoadedApk.java6
-rw-r--r--core/java/android/app/PendingIntent.java15
-rw-r--r--core/java/android/app/SharedElementCallback.java46
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java18
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl1
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java39
-rw-r--r--core/java/android/bluetooth/le/ScanCallback.java10
-rw-r--r--core/java/android/bluetooth/le/ScanFilter.java10
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java113
-rw-r--r--core/java/android/content/Context.java35
-rw-r--r--core/java/android/content/ContextWrapper.java11
-rw-r--r--core/java/android/content/IntentFilter.java8
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/PackageManager.java44
-rw-r--r--core/java/android/content/pm/PackageParser.java18
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java123
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java123
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java513
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl23
-rw-r--r--core/java/android/net/ConnectivityManager.java135
-rw-r--r--core/java/android/net/DhcpStateMachine.java177
-rw-r--r--core/java/android/net/Network.java12
-rw-r--r--core/java/android/net/NetworkUtils.java2
-rw-r--r--core/java/android/net/Proxy.java16
-rw-r--r--core/java/android/net/VpnService.java2
-rw-r--r--core/java/android/os/BatteryStats.java14
-rw-r--r--core/java/android/os/IHardwareService.aidl26
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/UserManager.java15
-rw-r--r--core/java/android/os/storage/DiskInfo.java10
-rw-r--r--core/java/android/os/storage/StorageManager.java31
-rw-r--r--core/java/android/os/storage/VolumeInfo.java18
-rw-r--r--core/java/android/provider/DocumentsContract.java17
-rw-r--r--core/java/android/security/keymaster/KeyCharacteristics.java55
-rw-r--r--core/java/android/security/keymaster/KeymasterArguments.java9
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java12
-rw-r--r--core/java/android/service/dreams/DreamService.java7
-rw-r--r--core/java/android/speech/tts/TextToSpeech.java6
-rw-r--r--core/java/android/view/PhoneWindow.java11
-rw-r--r--core/java/android/view/SearchEvent.java40
-rw-r--r--core/java/android/view/View.java77
-rw-r--r--core/java/android/view/ViewGroup.java20
-rw-r--r--core/java/android/view/Window.java9
-rw-r--r--core/java/android/view/WindowCallbackWrapper.java5
-rw-r--r--core/java/android/view/animation/AnimationUtils.java85
-rwxr-xr-xcore/java/android/widget/DatePickerCalendarDelegate.java12
-rw-r--r--core/java/android/widget/DayPickerView.java92
-rw-r--r--core/java/android/widget/Editor.java37
-rw-r--r--core/java/android/widget/ProgressBar.java79
-rw-r--r--core/java/android/widget/SimpleMonthView.java18
-rw-r--r--core/java/android/widget/TextView.java48
-rw-r--r--core/java/android/widget/YearPickerView.java27
-rw-r--r--core/java/com/android/internal/app/AlertController.java59
-rw-r--r--core/java/com/android/internal/logging/MetricsConstants.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java18
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java17
-rw-r--r--core/java/com/android/internal/os/WifiPowerCalculator.java7
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java66
-rw-r--r--core/java/com/android/internal/widget/DialogViewAnimator.java141
-rw-r--r--core/java/com/android/internal/widget/ViewPager.java2
71 files changed, 2398 insertions, 556 deletions
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index da48709..02a329d 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,7 +16,12 @@
package android.animation;
+import android.content.res.Configuration;
import android.content.res.ConstantState;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.animation.AnimationUtils;
import java.util.ArrayList;
@@ -25,6 +30,29 @@ import java.util.ArrayList;
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
+ /**
+ * Set this hint when duration for the animation does not need to be scaled. By default, no
+ * scaling is applied to the duration.
+ */
+ public static final int HINT_NO_SCALE = 0;
+
+ /**
+ * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)} when the animation's
+ * moving distance is proportional to the screen size. (e.g. a view coming in from the bottom of
+ * the screen to top/center). With this scale hint set, the animation duration will be
+ * automatically scaled based on screen size.
+ */
+ public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1;
+
+ /**
+ * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)}) if the animation
+ * has pre-defined moving distance in dp that does not vary from device to device. This is
+ * extremely useful when the animation needs to run on both phones/tablets and TV, because TV
+ * has inflated dp and therefore will have a longer visual arc for the same animation than on
+ * the phone. This hint is used to calculate a scaling factor to compensate for different
+ * visual arcs while maintaining the same angular velocity for the animation.
+ */
+ public static final int HINT_DISTANCE_DEFINED_IN_DP = 2;
/**
* The set of listeners to be sent events through the life of an animation.
@@ -55,6 +83,24 @@ public abstract class Animator implements Cloneable {
private AnimatorConstantState mConstantState;
/**
+ * Scaling factor for an animation that moves across the whole screen.
+ */
+ float mScreenSizeBasedDurationScale = 1.0f;
+
+ /**
+ * Scaling factor for an animation that is defined to move the same amount of dp across all
+ * devices.
+ */
+ float mDpBasedDurationScale = 1.0f;
+
+ /**
+ * By default, the scaling assumes the animation moves across the entire screen.
+ */
+ int mDurationScaleHint = HINT_NO_SCALE;
+
+ private final static boolean ANIM_DEBUG = false;
+
+ /**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
@@ -184,6 +230,78 @@ public abstract class Animator implements Cloneable {
public abstract long getDuration();
/**
+ * Hints how duration scaling factor should be calculated. The duration will not be scaled when
+ * hint is set to {@link #HINT_NO_SCALE}. Otherwise, the duration will be automatically scaled
+ * per device to achieve the same look and feel across different devices. In order to do
+ * that, the same angular velocity of the animation will be needed on different devices in
+ * users' field of view. Therefore, the duration scale factor is determined by the ratio of the
+ * angular movement on current devices to that on the baseline device (i.e. Nexus 5).
+ *
+ * @param hint an indicator on how the animation is defined. The hint could be
+ * {@link #HINT_NO_SCALE}, {@link #HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE} or
+ * {@link #HINT_DISTANCE_DEFINED_IN_DP}.
+ * @param res The resources {@see android.content.res.Resources} for getting display metrics
+ */
+ public void setDurationScaleHint(int hint, Resources res) {
+ if (ANIM_DEBUG) {
+ Log.d("ANIM_DEBUG", "distance based duration hint: " + hint);
+ }
+ if (hint == mDurationScaleHint) {
+ return;
+ }
+ mDurationScaleHint = hint;
+ if (hint != HINT_NO_SCALE) {
+ int uiMode = res.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK;
+ DisplayMetrics metrics = res.getDisplayMetrics();
+ float width = metrics.widthPixels / metrics.xdpi;
+ float height = metrics.heightPixels / metrics.ydpi;
+ float viewingDistance = AnimationUtils.getViewingDistance(width, height, uiMode);
+ if (ANIM_DEBUG) {
+ Log.d("ANIM_DEBUG", "width, height, viewing distance, uimode: "
+ + width + ", " + height + ", " + viewingDistance + ", " + uiMode);
+ }
+ mScreenSizeBasedDurationScale = AnimationUtils
+ .getScreenSizeBasedDurationScale(width, height, viewingDistance);
+ mDpBasedDurationScale = AnimationUtils.getDpBasedDurationScale(
+ metrics.density, metrics.xdpi, viewingDistance);
+ if (ANIM_DEBUG) {
+ Log.d("ANIM_DEBUG", "screen based scale, dp based scale: " +
+ mScreenSizeBasedDurationScale + ", " + mDpBasedDurationScale);
+ }
+ }
+ }
+
+ // Copies duration scale hint and scaling factors to the new animation.
+ void copyDurationScaleInfoTo(Animator anim) {
+ anim.mDurationScaleHint = mDurationScaleHint;
+ anim.mScreenSizeBasedDurationScale = mScreenSizeBasedDurationScale;
+ anim.mDpBasedDurationScale = mDpBasedDurationScale;
+ }
+
+ /**
+ * @return The scaled duration calculated based on distance of movement (as defined by the
+ * animation) and perceived velocity (derived from the duration set on the animation for
+ * baseline device)
+ */
+ public long getDistanceBasedDuration() {
+ return (long) (getDuration() * getDistanceBasedDurationScale());
+ }
+
+ /**
+ * @return scaling factor of duration based on the duration scale hint. A scaling factor of 1
+ * means no scaling will be applied to the duration.
+ */
+ float getDistanceBasedDurationScale() {
+ if (mDurationScaleHint == HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE) {
+ return mScreenSizeBasedDurationScale;
+ } else if (mDurationScaleHint == HINT_DISTANCE_DEFINED_IN_DP) {
+ return mDpBasedDurationScale;
+ } else {
+ return 1f;
+ }
+ }
+
+ /**
* The time interpolator used in calculating the elapsed fraction of the
* animation. The interpolator determines whether the animation runs with
* linear or non-linear motion, such as acceleration and deceleration. The
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 4a9ba3b..df5a4cb 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -70,6 +70,13 @@ public class AnimatorInflater {
private static final int VALUE_TYPE_COLOR = 3;
private static final int VALUE_TYPE_UNDEFINED = 4;
+ /**
+ * Enum values used in XML attributes to indicate the duration scale hint.
+ */
+ private static final int HINT_NO_SCALE = 0;
+ private static final int HINT_PROPORTIONAL_TO_SCREEN = 1;
+ private static final int HINT_DEFINED_IN_DP = 2;
+
private static final boolean DBG_ANIMATOR_INFLATER = false;
// used to calculate changing configs for resource references
@@ -691,6 +698,9 @@ public class AnimatorInflater {
int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
pixelSize);
+ final int hint = a.getInt(R.styleable.Animator_durationScaleHint,
+ HINT_NO_SCALE);
+ anim.setDurationScaleHint(hint, res);
a.recycle();
} else if (name.equals("propertyValuesHolder")) {
PropertyValuesHolder[] values = loadValues(res, theme, parser,
@@ -1027,6 +1037,9 @@ public class AnimatorInflater {
anim.setInterpolator(interpolator);
}
+ final int hint = arrayAnimator.getInt(R.styleable.Animator_durationScaleHint,
+ HINT_NO_SCALE);
+ anim.setDurationScaleHint(hint, res);
arrayAnimator.recycle();
if (arrayObjectAnimator != null) {
arrayObjectAnimator.recycle();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 53d5237..dd5f18e 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -519,6 +519,7 @@ public final class AnimatorSet extends Animator {
for (Node node : mNodes) {
node.animation.setAllowRunningAsynchronously(false);
+ copyDurationScaleInfoTo(node.animation);
}
if (mDuration >= 0) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 2386007..275e78e 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,9 +17,12 @@
package android.animation;
import android.annotation.CallSuper;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Choreographer;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -561,7 +564,7 @@ public class ValueAnimator extends Animator {
}
private void updateScaledDuration() {
- mDuration = (long)(mUnscaledDuration * sDurationScale);
+ mDuration = (long)(mUnscaledDuration * sDurationScale * getDistanceBasedDurationScale());
}
/**
@@ -1478,6 +1481,12 @@ public class ValueAnimator extends Animator {
anim.mInitialized = false;
anim.mPlayingState = STOPPED;
anim.mStartedDelay = false;
+ anim.mStarted = false;
+ anim.mRunning = false;
+ anim.mPaused = false;
+ anim.mResumed = false;
+ anim.mStartListenersCalled = false;
+
PropertyValuesHolder[] oldValues = mValues;
if (oldValues != null) {
int numValues = oldValues.length;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b5817df..6cf6481 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -89,11 +89,13 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.PhoneWindow;
+import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewManager;
+import android.view.ViewRootImpl;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -786,6 +788,8 @@ public class Activity extends ContextThemeWrapper
private TranslucentConversionListener mTranslucentCallback;
private boolean mChangeCanvasToTranslucent;
+ private SearchEvent mSearchEvent;
+
private boolean mTitleReady = false;
private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
@@ -3546,12 +3550,23 @@ public class Activity extends ContextThemeWrapper
* implementation changes to simply return false and you must supply your own custom
* implementation if you want to support search.</p>
*
+ * @param searchEvent The {@link SearchEvent} that signaled this search.
* @return Returns {@code true} if search launched, and {@code false} if the activity does
* not respond to search. The default implementation always returns {@code true}, except
* when in {@link Configuration#UI_MODE_TYPE_TELEVISION} mode where it returns false.
*
* @see android.app.SearchManager
*/
+ public boolean onSearchRequested(@Nullable SearchEvent searchEvent) {
+ mSearchEvent = searchEvent;
+ boolean result = onSearchRequested();
+ mSearchEvent = null;
+ return result;
+ }
+
+ /**
+ * @see #onSearchRequested(SearchEvent)
+ */
public boolean onSearchRequested() {
if ((getResources().getConfiguration().uiMode&Configuration.UI_MODE_TYPE_MASK)
!= Configuration.UI_MODE_TYPE_TELEVISION) {
@@ -3563,6 +3578,17 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * During the onSearchRequested() callbacks, this function will return the
+ * {@link SearchEvent} that triggered the callback, if it exists.
+ *
+ * @return SearchEvent The SearchEvent that triggered the {@link
+ * #onSearchRequested} callback.
+ */
+ public final SearchEvent getSearchEvent() {
+ return mSearchEvent;
+ }
+
+ /**
* This hook is called to launch the search UI.
*
* <p>It is typically called from onSearchRequested(), either directly from
@@ -4467,21 +4493,38 @@ public class Activity extends ContextThemeWrapper
*/
public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
int requestCode, @Nullable Bundle options) {
+ startActivityForResult(fragment.mWho, intent, requestCode, options);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void startActivityForResult(
+ String who, Intent intent, int requestCode, @Nullable Bundle options) {
if (options != null) {
mActivityTransitionState.startExitOutTransition(this, options);
}
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
- this, mMainThread.getApplicationThread(), mToken, fragment,
+ this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
- mToken, fragment.mWho, requestCode,
+ mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
}
/**
+ * @hide
+ */
+ @Override
+ public boolean canStartActivityForResult() {
+ return true;
+ }
+
+ /**
* Same as calling {@link #startIntentSenderFromChild(Activity, IntentSender,
* int, Intent, int, int, int, Bundle)} with no options.
*/
@@ -6364,12 +6407,24 @@ public class Activity extends ContextThemeWrapper
onActivityResult(requestCode, resultCode, data);
}
} else {
- Fragment frag = mFragments.findFragmentByWho(who);
- if (frag != null) {
- if (isRequestPermissionResult(data)) {
- dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
- } else {
- frag.onActivityResult(requestCode, resultCode, data);
+ if (who.startsWith("@android:view:")) {
+ ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
+ getActivityToken());
+ for (ViewRootImpl viewRoot : views) {
+ if (viewRoot.getView() != null
+ && viewRoot.getView().dispatchActivityResult(
+ who, requestCode, resultCode, data)) {
+ return;
+ }
+ }
+ } else {
+ Fragment frag = mFragments.findFragmentByWho(who);
+ if (frag != null) {
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
+ } else {
+ frag.onActivityResult(requestCode, resultCode, data);
+ }
}
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3b96fd5..10d6d01 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -220,6 +220,7 @@ public final class ActivityThread {
// which means this lock gets held while the activity and window managers
// holds their own lock. Thus you MUST NEVER call back into the activity manager
// or window manager or anything that depends on them while holding this lock.
+ // These LoadedApk are only valid for the userId that we're running as.
final ArrayMap<String, WeakReference<LoadedApk>> mPackages
= new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
@@ -258,6 +259,8 @@ public final class ActivityThread {
IActivityManager.ContentProviderHolder holder;
boolean acquiring = true;
int requests = 1;
+ // Set if there was a runtime exception when trying to acquire the provider.
+ RuntimeException runtimeException = null;
}
// The lock of mProviderMap protects the following variables.
@@ -864,10 +867,10 @@ public final class ActivityThread {
}
public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
- final Network network = ConnectivityManager.getProcessDefaultNetwork();
+ final ConnectivityManager cm = ConnectivityManager.from(getSystemContext());
+ final Network network = cm.getBoundNetworkForProcess();
if (network != null) {
- Proxy.setHttpProxySystemProperty(
- ConnectivityManager.from(getSystemContext()).getDefaultProxy());
+ Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
} else {
Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
@@ -1705,13 +1708,18 @@ public final class ActivityThread {
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags, int userId) {
+ final boolean differentUser = (UserHandle.myUserId() != userId);
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
- if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
+ if (differentUser) {
+ // Caching not supported across users
+ ref = null;
+ } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) {
ref = mPackages.get(packageName);
} else {
ref = mResourcePackages.get(packageName);
}
+
LoadedApk packageInfo = ref != null ? ref.get() : null;
//Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
//if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
@@ -1791,13 +1799,18 @@ public final class ActivityThread {
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
+ final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
- if (includeCode) {
+ if (differentUser) {
+ // Caching not supported across users
+ ref = null;
+ } else if (includeCode) {
ref = mPackages.get(aInfo.packageName);
} else {
ref = mResourcePackages.get(aInfo.packageName);
}
+
LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null
&& !packageInfo.mResources.getAssets().isUpToDate())) {
@@ -1816,7 +1829,9 @@ public final class ActivityThread {
getSystemContext().mPackageInfo.getClassLoader());
}
- if (includeCode) {
+ if (differentUser) {
+ // Caching not supported across users
+ } else if (includeCode) {
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
@@ -4715,39 +4730,55 @@ public final class ActivityThread {
}
IActivityManager.ContentProviderHolder holder = null;
- if (first) {
- // Multiple threads may try to acquire the same provider at the same time.
- // When this happens, we only let the first one really gets provider.
- // Other threads just wait for its result.
- // Note that we cannot hold the lock while acquiring and installing the
- // provider since it might take a long time to run and it could also potentially
- // be re-entrant in the case where the provider is in the same process.
- try {
+ try {
+ if (first) {
+ // Multiple threads may try to acquire the same provider at the same time.
+ // When this happens, we only let the first one really gets provider.
+ // Other threads just wait for its result.
+ // Note that we cannot hold the lock while acquiring and installing the
+ // provider since it might take a long time to run and it could also potentially
+ // be re-entrant in the case where the provider is in the same process.
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
- } catch (RemoteException ex) {
+ } else {
+ synchronized (r) {
+ while (r.acquiring) {
+ try {
+ r.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ holder = r.holder;
+ }
}
+ } catch (RemoteException ex) {
+ } catch (RuntimeException e) {
synchronized (r) {
- r.holder = holder;
- r.acquiring = false;
- r.notifyAll();
+ r.runtimeException = e;
}
- } else {
- synchronized (r) {
- while (r.acquiring) {
- try {
- r.wait();
- } catch (InterruptedException e) {
- }
+ } finally {
+ if (first) {
+ synchronized (r) {
+ r.holder = holder;
+ r.acquiring = false;
+ r.notifyAll();
}
- holder = r.holder;
}
- }
- synchronized (mAcquiringProviderMap) {
- if (--r.requests == 0) {
- mAcquiringProviderMap.remove(key);
+
+ synchronized (mAcquiringProviderMap) {
+ if (--r.requests == 0) {
+ mAcquiringProviderMap.remove(key);
+ }
+ }
+
+ if (r.runtimeException != null) {
+ // Was set when the first thread tried to acquire the provider,
+ // but we should make sure it is thrown for all threads trying to
+ // acquire the provider.
+ throw r.runtimeException;
}
}
+
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ffdc81d..9ddfd88 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1359,6 +1359,26 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
+ public String getDefaultBrowserPackageName(int userId) {
+ try {
+ return mPM.getDefaultBrowserPackageName(userId);
+ } catch (RemoteException e) {
+ // Should never happen!
+ return null;
+ }
+ }
+
+ @Override
+ public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+ try {
+ return mPM.setDefaultBrowserPackageName(packageName, userId);
+ } catch (RemoteException e) {
+ // Should never happen!
+ return false;
+ }
+ }
+
+ @Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
try {
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 83451aa..8fb048b 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -1368,7 +1368,6 @@ final class BackStackRecord extends FragmentTransaction implements
public boolean onPreDraw() {
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
if (enterTransition != null) {
- enterTransition.removeTarget(nonExistingView);
removeTargets(enterTransition, enteringViews);
}
if (exitTransition != null) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 786a52f..d049104 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -49,6 +49,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.PhoneWindow;
+import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
@@ -123,6 +124,8 @@ public class Dialog implements DialogInterface, Window.Callback,
private Handler mListenersHandler;
+ private SearchEvent mSearchEvent;
+
private ActionMode mActionMode;
private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
@@ -995,6 +998,14 @@ public class Dialog implements DialogInterface, Window.Callback,
/**
* This hook is called when the user signals the desire to start a search.
*/
+ public boolean onSearchRequested(SearchEvent searchEvent) {
+ mSearchEvent = searchEvent;
+ return onSearchRequested();
+ }
+
+ /**
+ * This hook is called when the user signals the desire to start a search.
+ */
public boolean onSearchRequested() {
final SearchManager searchManager = (SearchManager) mContext
.getSystemService(Context.SEARCH_SERVICE);
@@ -1011,6 +1022,17 @@ public class Dialog implements DialogInterface, Window.Callback,
}
/**
+ * During the onSearchRequested() callbacks, this function will return the
+ * {@link SearchEvent} that triggered the callback, if it exists.
+ *
+ * @return SearchEvent The SearchEvent that triggered the {@link
+ * #onSearchRequested} callback.
+ */
+ public final SearchEvent getSearchEvent() {
+ return mSearchEvent;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl
index 845897b..caee14f 100644
--- a/core/java/android/app/IUserSwitchObserver.aidl
+++ b/core/java/android/app/IUserSwitchObserver.aidl
@@ -22,4 +22,5 @@ import android.os.IRemoteCallback;
oneway interface IUserSwitchObserver {
void onUserSwitching(int newUserId, IRemoteCallback reply);
void onUserSwitchComplete(int newUserId);
+ void onForegroundProfileSwitch(int newProfileId);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 5572d30..fc96464 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1568,7 +1568,7 @@ public class Instrumentation {
/**
* Like {@link #execStartActivity(android.content.Context, android.os.IBinder,
- * android.os.IBinder, Fragment, android.content.Intent, int, android.os.Bundle)},
+ * android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)},
* but for calls from a {#link Fragment}.
*
* @param who The Context from which the activity is being started.
@@ -1576,7 +1576,7 @@ public class Instrumentation {
* is being started.
* @param token Internal token identifying to the system who is starting
* the activity; may be null.
- * @param target Which fragment is performing the start (and thus receiving
+ * @param target Which element is performing the start (and thus receiving
* any result).
* @param intent The actual Intent to start.
* @param requestCode Identifier for this request's result; less than zero
@@ -1595,7 +1595,7 @@ public class Instrumentation {
* {@hide}
*/
public ActivityResult execStartActivity(
- Context who, IBinder contextThread, IBinder token, Fragment target,
+ Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
@@ -1619,8 +1619,7 @@ public class Instrumentation {
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, target != null ? target.mWho : null,
- requestCode, 0, null, options);
+ token, target, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 83c6c2b..9604789 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -18,7 +18,6 @@ package android.app;
import android.text.TextUtils;
import android.util.ArrayMap;
-
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -46,6 +45,7 @@ import android.util.SparseArray;
import android.view.DisplayAdjustments;
import android.view.Display;
import android.os.SystemProperties;
+
import dalvik.system.VMRuntime;
import java.io.File;
@@ -136,10 +136,6 @@ public final class LoadedApk {
mSplitAppDirs = aInfo.splitSourceDirs;
mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
mOverlayDirs = aInfo.resourceDirs;
- if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
- aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
- mPackageName);
- }
mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cf14202..2cfc1fa4 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -151,6 +151,14 @@ public final class PendingIntent implements Parcelable {
public static final int FLAG_UPDATE_CURRENT = 1<<27;
/**
+ * Flag indicating that the created PendingIntent should be immutable.
+ * This means that the additional intent argument passed to the send
+ * methods to fill in unpopulated properties of this intent will be
+ * ignored.
+ */
+ public static final int FLAG_IMMUTABLE = 1<<26;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -618,7 +626,8 @@ public final class PendingIntent implements Parcelable {
* @param code Result code to supply back to the PendingIntent's target.
* @param intent Additional Intent data. See {@link Intent#fillIn
* Intent.fillIn()} for information on how this is applied to the
- * original Intent.
+ * original Intent. If flag {@link #FLAG_IMMUTABLE} was set when this
+ * pending intent was created, this argument will be ignored.
*
* @see #send(Context, int, Intent, android.app.PendingIntent.OnFinished, Handler)
*
@@ -667,6 +676,8 @@ public final class PendingIntent implements Parcelable {
* @param intent Additional Intent data. See {@link Intent#fillIn
* Intent.fillIn()} for information on how this is applied to the
* original Intent. Use null to not modify the original Intent.
+ * If flag {@link #FLAG_IMMUTABLE} was set when this pending intent was
+ * created, this argument will be ignored.
* @param onFinished The object to call back on when the send has
* completed, or null for no callback.
* @param handler Handler identifying the thread on which the callback
@@ -703,6 +714,8 @@ public final class PendingIntent implements Parcelable {
* @param intent Additional Intent data. See {@link Intent#fillIn
* Intent.fillIn()} for information on how this is applied to the
* original Intent. Use null to not modify the original Intent.
+ * If flag {@link #FLAG_IMMUTABLE} was set when this pending intent was
+ * created, this argument will be ignored.
* @param onFinished The object to call back on when the send has
* completed, or null for no callback.
* @param handler Handler identifying the thread on which the callback
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index e58b7fb..bac84a4 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -51,9 +51,23 @@ public abstract class SharedElementCallback {
};
/**
- * Called immediately after the start state is set for the shared element.
- * The shared element will start at the size and position of the shared element
- * in the launching Activity or Fragment.
+ * In Activity Transitions, onSharedElementStart is called immediately before
+ * capturing the start of the shared element state on enter and reenter transitions and
+ * immediately before capturing the end of the shared element state for exit and return
+ * transitions.
+ * <p>
+ * In Fragment Transitions, onSharedElementStart is called immediately before capturing the
+ * start state of all shared element transitions.
+ * <p>
+ * This call can be used to adjust the transition start state by modifying the shared
+ * element Views. Note that no layout step will be executed between onSharedElementStart
+ * and the transition state capture.
+ * <p>
+ * For Activity Transitions, any changes made in {@link #onSharedElementEnd(List, List, List)}
+ * that are not updated during by layout should be corrected in onSharedElementStart for exit and
+ * return transitions. For example, rotation or scale will not be affected by layout and
+ * if changed in {@link #onSharedElementEnd(List, List, List)}, it will also have to be reset
+ * in onSharedElementStart again to correct the end state.
*
* @param sharedElementNames The names of the shared elements that were accepted into
* the View hierarchy.
@@ -68,17 +82,23 @@ public abstract class SharedElementCallback {
List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
- * Called after the end state is set for the shared element, but before the end state
- * is captured by the shared element transition.
+ * In Activity Transitions, onSharedElementEnd is called immediately before
+ * capturing the end of the shared element state on enter and reenter transitions and
+ * immediately before capturing the start of the shared element state for exit and return
+ * transitions.
* <p>
- * Any customization done in
- * {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)}
- * may need to be modified to the final state of the shared element if it is not
- * automatically corrected by layout. For example, rotation or scale will not
- * be affected by layout and if changed in {@link #onSharedElementStart(java.util.List,
- * java.util.List, java.util.List)}, it will also have to be set here again to correct
- * the end state.
- * </p>
+ * In Fragment Transitions, onSharedElementEnd is called immediately before capturing the
+ * end state of all shared element transitions.
+ * <p>
+ * This call can be used to adjust the transition end state by modifying the shared
+ * element Views. Note that no layout step will be executed between onSharedElementEnd
+ * and the transition state capture.
+ * <p>
+ * Any changes made in {@link #onSharedElementStart(List, List, List)} that are not updated
+ * during layout should be corrected in onSharedElementEnd. For example, rotation or scale
+ * will not be affected by layout and if changed in
+ * {@link #onSharedElementStart(List, List, List)}, it will also have to be reset in
+ * onSharedElementEnd again to correct the end state.
*
* @param sharedElementNames The names of the shared elements that were accepted into
* the View hierarchy.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index edb768d..2b3cf34 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -995,6 +995,24 @@ public final class BluetoothAdapter {
}
/**
+ * Return true if hardware has entries available for matching beacons
+ *
+ * @return true if there are hw entries available for matching beacons
+ * @hide
+ */
+ public boolean isHardwareTrackingFiltersAvailable() {
+ if (getState() != STATE_ON) return false;
+ try {
+ synchronized(mManagerCallback) {
+ if(mService != null) return (mService.numOfHwTrackFiltersAvailable() != 0);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
+ }
+
+ /**
* Return the record of {@link BluetoothActivityEnergyInfo} object that
* has the activity and energy info. This can be used to ascertain what
* the controller has been up to, since the last sample.
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index dabb1ce..299f4c8 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -98,6 +98,7 @@ interface IBluetooth
boolean isActivityAndEnergyReportingSupported();
void getActivityEnergyInfoFromController();
BluetoothActivityEnergyInfo reportActivityInfo();
+ int numOfHwTrackFiltersAvailable();
// for dumpsys support
String dump();
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 5dbfa6a..b6bcfb8 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -128,6 +128,16 @@ public final class BluetoothLeScanner {
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
return;
}
+ if (!isHardwareResourcesAvailableForScan(settings)) {
+ postCallbackError(callback,
+ ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
+ return;
+ }
+ if (!isSettingsAndFilterComboAllowed(settings, filters)) {
+ postCallbackError(callback,
+ ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
+ return;
+ }
BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
settings, callback, resultStorages);
wrapper.startRegisteration();
@@ -394,4 +404,33 @@ public final class BluetoothLeScanner {
}
return false;
}
+
+ private boolean isSettingsAndFilterComboAllowed(ScanSettings settings,
+ List <ScanFilter> filterList) {
+ final int callbackType = settings.getCallbackType();
+ // If onlost/onfound is requested, a non-empty filter is expected
+ if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH
+ | ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
+ if (filterList == null) {
+ return false;
+ }
+ for (ScanFilter filter : filterList) {
+ if (filter.isAllFieldsEmpty()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
+ final int callbackType = settings.getCallbackType();
+ if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
+ || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
+ // For onlost/onfound, we required hw support be available
+ return (mBluetoothAdapter.isOffloadedFilteringSupported() &&
+ mBluetoothAdapter.isHardwareTrackingFiltersAvailable());
+ }
+ return true;
+ }
}
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index 05782a8..27b96bd 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -45,10 +45,16 @@ public abstract class ScanCallback {
public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4;
/**
+ * Fails to start scan as it is out of hardware resources.
+ * @hide
+ */
+ public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
+
+ /**
* Callback when a BLE advertisement has been found.
*
- * @param callbackType Determines how this callback was triggered. Currently could only be
- * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}.
+ * @param callbackType Determines how this callback was triggered. Could be of
+ * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}
* @param result A Bluetooth LE scan result.
*/
public void onScanResult(int callbackType, ScanResult result) {
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 5025218..92a3817 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -67,6 +67,8 @@ public final class ScanFilter implements Parcelable {
private final byte[] mManufacturerData;
@Nullable
private final byte[] mManufacturerDataMask;
+ private static final ScanFilter EMPTY = new ScanFilter.Builder().build() ;
+
private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
ParcelUuid uuidMask, ParcelUuid serviceDataUuid,
@@ -410,6 +412,14 @@ public final class ScanFilter implements Parcelable {
}
/**
+ * Checks if the scanfilter is empty
+ * @hide
+ */
+ public boolean isAllFieldsEmpty() {
+ return EMPTY.equals(this);
+ }
+
+ /**
* Builder class for {@link ScanFilter}.
*/
public static final class Builder {
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 0106686..f103cae 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -59,7 +59,6 @@ public final class ScanSettings implements Parcelable {
/**
* A result callback is only triggered for the first advertisement packet received that matches
* the filter criteria.
- *
* @hide
*/
@SystemApi
@@ -68,15 +67,53 @@ public final class ScanSettings implements Parcelable {
/**
* Receive a callback when advertisements are no longer received from a device that has been
* previously reported by a first match callback.
- *
* @hide
*/
@SystemApi
public static final int CALLBACK_TYPE_MATCH_LOST = 4;
+
+ /**
+ * Determines how many advertisements to match per filter, as this is scarce hw resource
+ */
+ /**
+ * Match one advertisement per filter
+ * @hide
+ */
+ public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
+
+ /**
+ * Match few advertisement per filter, depends on current capability and availibility of
+ * the resources in hw
+ * @hide
+ */
+ public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
+
+ /**
+ * Match as many advertisement per filter as hw could allow, depends on current
+ * capability and availibility of the resources in hw
+ * @hide
+ */
+ public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
+
+
+ /**
+ * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
+ * and few number of sightings/match in a duration.
+ * @hide
+ */
+ public static final int MATCH_MODE_AGGRESSIVE = 1;
+
/**
- * Request full scan results which contain the device, rssi, advertising data, scan response as
- * well as the scan timestamp.
+ * For sticky mode, higher threshold of signal strength and sightings is required
+ * before reporting by hw
+ * @hide
+ */
+ public static final int MATCH_MODE_STICKY = 2;
+
+ /**
+ * Request full scan results which contain the device, rssi, advertising data, scan response
+ * as well as the scan timestamp.
*
* @hide
*/
@@ -106,6 +143,10 @@ public final class ScanSettings implements Parcelable {
// Time of delay for reporting the scan result
private long mReportDelayMillis;
+ private int mMatchMode;
+
+ private int mNumOfMatchesPerFilter;
+
public int getScanMode() {
return mScanMode;
}
@@ -119,6 +160,20 @@ public final class ScanSettings implements Parcelable {
}
/**
+ * @hide
+ */
+ public int getMatchMode() {
+ return mMatchMode;
+ }
+
+ /**
+ * @hide
+ */
+ public int getNumOfMatches() {
+ return mNumOfMatchesPerFilter;
+ }
+
+ /**
* Returns report delay timestamp based on the device clock.
*/
public long getReportDelayMillis() {
@@ -126,11 +181,13 @@ public final class ScanSettings implements Parcelable {
}
private ScanSettings(int scanMode, int callbackType, int scanResultType,
- long reportDelayMillis) {
+ long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) {
mScanMode = scanMode;
mCallbackType = callbackType;
mScanResultType = scanResultType;
mReportDelayMillis = reportDelayMillis;
+ mNumOfMatchesPerFilter = numOfMatchesPerFilter;
+ mMatchMode = numOfMatchesPerFilter;
}
private ScanSettings(Parcel in) {
@@ -138,6 +195,8 @@ public final class ScanSettings implements Parcelable {
mCallbackType = in.readInt();
mScanResultType = in.readInt();
mReportDelayMillis = in.readLong();
+ mMatchMode = in.readInt();
+ mNumOfMatchesPerFilter = in.readInt();
}
@Override
@@ -146,6 +205,8 @@ public final class ScanSettings implements Parcelable {
dest.writeInt(mCallbackType);
dest.writeInt(mScanResultType);
dest.writeLong(mReportDelayMillis);
+ dest.writeInt(mMatchMode);
+ dest.writeInt(mNumOfMatchesPerFilter);
}
@Override
@@ -174,7 +235,8 @@ public final class ScanSettings implements Parcelable {
private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
private int mScanResultType = SCAN_RESULT_TYPE_FULL;
private long mReportDelayMillis = 0;
-
+ private int mMatchMode = MATCH_MODE_AGGRESSIVE;
+ private int mNumOfMatchesPerFilter = MATCH_NUM_ONE_ADVERTISEMENT;
/**
* Set scan mode for Bluetooth LE scan.
*
@@ -255,11 +317,48 @@ public final class ScanSettings implements Parcelable {
}
/**
+ * Set the number of matches for Bluetooth LE scan filters hardware match
+ *
+ * @param numOfMatches The num of matches can be one of
+ * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or
+ * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
+ * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
+ * @throws IllegalArgumentException If the {@code matchMode} is invalid.
+ * @hide
+ */
+ public Builder setNumOfMatches(int numOfMatches) {
+ if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
+ || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
+ throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
+ }
+ mNumOfMatchesPerFilter = numOfMatches;
+ return this;
+ }
+
+ /**
+ * Set match mode for Bluetooth LE scan filters hardware match
+ *
+ * @param matchMode The match mode can be one of
+ * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
+ * {@link ScanSettings#MATCH_MODE_STICKY}
+ * @throws IllegalArgumentException If the {@code matchMode} is invalid.
+ * @hide
+ */
+ public Builder setMatchMode(int matchMode) {
+ if (matchMode < MATCH_MODE_AGGRESSIVE
+ || matchMode > MATCH_MODE_STICKY) {
+ throw new IllegalArgumentException("invalid matchMode " + matchMode);
+ }
+ mMatchMode = matchMode;
+ return this;
+ }
+
+ /**
* Build {@link ScanSettings}.
*/
public ScanSettings build() {
return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
- mReportDelayMillis);
+ mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter);
}
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9450dce..e5e55d6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1298,6 +1298,34 @@ public abstract class Context {
}
/**
+ * Version of {@link #startActivity(Intent, Bundle)} that returns a result to the caller. This
+ * is only supported for Views and Fragments.
+ * @param who The identifier for the calling element that will receive the result.
+ * @param intent The intent to start.
+ * @param requestCode The code that will be returned with onActivityResult() identifying this
+ * request.
+ * @param options Additional options for how the Activity should be started.
+ * May be null if there are no options. See {@link android.app.ActivityOptions}
+ * for how to build the Bundle supplied here; there are no supported definitions
+ * for building it manually.
+ * @hide
+ */
+ public void startActivityForResult(
+ @NonNull String who, Intent intent, int requestCode, @Nullable Bundle options) {
+ throw new RuntimeException("This method is only implemented for Activity-based Contexts. "
+ + "Check canStartActivityForResult() before calling.");
+ }
+
+ /**
+ * Identifies whether this Context instance will be able to process calls to
+ * {@link #startActivityForResult(String, Intent, int, Bundle)}.
+ * @hide
+ */
+ public boolean canStartActivityForResult() {
+ return false;
+ }
+
+ /**
* Same as {@link #startActivities(Intent[], Bundle)} with no options
* specified.
*
@@ -2161,7 +2189,7 @@ public abstract class Context {
WIFI_RTT_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
- //@hide: FINGERPRINT_SERVICE,
+ FINGERPRINT_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -2668,12 +2696,11 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.service.fingerprint.FingerprintManager} for handling management
+ * {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
*
* @see #getSystemService
- * @see android.service.fingerprint.FingerprintManager
- * @hide
+ * @see android.hardware.fingerprint.FingerprintManager
*/
public static final String FINGERPRINT_SERVICE = "fingerprint";
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 8c5a87c..92f0079 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -336,6 +336,17 @@ public class ContextWrapper extends Context {
mBase.startActivityAsUser(intent, user);
}
+ /** @hide **/
+ public void startActivityForResult(
+ String who, Intent intent, int requestCode, Bundle options) {
+ mBase.startActivityForResult(who, intent, requestCode, options);
+ }
+
+ /** @hide **/
+ public boolean canStartActivityForResult() {
+ return mBase.canStartActivityForResult();
+ }
+
@Override
public void startActivity(Intent intent, Bundle options) {
mBase.startActivity(intent, options);
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 044e3e3..33c0b87 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -521,14 +521,18 @@ public class IntentFilter implements Parcelable {
*
* @return True if the filter handle all HTTP or HTTPS data URI. False otherwise.
*
- * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+ * This will check if:
+ *
+ * - either the Intent category is {@link android.content.Intent#CATEGORY_APP_BROWSER}
+ * - either the Intent action is {@link android.content.Intent#ACTION_VIEW} and
* the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
* data scheme is "http" or "https" and that there is no specific host defined.
*
* @hide
*/
public final boolean handleAllWebDataURI() {
- return hasWebDataURI() && (countDataAuthorities() == 0);
+ return hasCategory(Intent.CATEGORY_APP_BROWSER) ||
+ (hasWebDataURI() && countDataAuthorities() == 0);
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 55c990f..c2580c0 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -450,6 +450,9 @@ interface IPackageManager {
List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
List<IntentFilter> getAllIntentFilters(String packageName);
+ boolean setDefaultBrowserPackageName(String packageName, int userId);
+ String getDefaultBrowserPackageName(int userId);
+
VerifierDeviceIdentity getVerifierDeviceIdentity();
boolean isFirstBoot();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d5461eb..8844ea8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -44,6 +44,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
+import android.text.TextUtils;
import android.util.AndroidException;
import com.android.internal.util.ArrayUtils;
@@ -3667,6 +3668,32 @@ public abstract class PackageManager {
public abstract List<IntentFilter> getAllIntentFilters(String packageName);
/**
+ * Get the default Browser package name for a specific user.
+ *
+ * @param userId The user id.
+ *
+ * @return the package name of the default Browser for the specified user. If the user id passed
+ * is -1 (all users) it will return a null value.
+ *
+ * @hide
+ */
+ public abstract String getDefaultBrowserPackageName(int userId);
+
+ /**
+ * Set the default Browser package name for a specific user.
+ *
+ * @param packageName The package name of the default Browser.
+ * @param userId The user id.
+ *
+ * @return true if the default Browser for the specified user has been set,
+ * otherwise return false. If the user id passed is -1 (all users) this call will not
+ * do anything and just return false.
+ *
+ * @hide
+ */
+ public abstract boolean setDefaultBrowserPackageName(String packageName, int userId);
+
+ /**
* Change the installer associated with a given package. There are limitations
* on how the installer package can be changed; in particular:
* <ul>
@@ -4158,16 +4185,19 @@ public abstract class PackageManager {
public abstract @NonNull PackageInstaller getPackageInstaller();
/**
- * Returns the data directory for a particular user and package, given the uid of the package.
- * @param uid uid of the package, including the userId and appId
- * @param packageName name of the package
- * @return the user-specific data directory for the package
+ * Returns the data directory for a particular package and user.
+ *
* @hide
*/
- public static String getDataDirForUser(int userId, String packageName) {
+ public static File getDataDirForUser(String volumeUuid, String packageName, int userId) {
// TODO: This should be shared with Installer's knowledge of user directory
- return Environment.getDataDirectory().toString() + "/user/" + userId
- + "/" + packageName;
+ final File base;
+ if (TextUtils.isEmpty(volumeUuid)) {
+ base = Environment.getDataDirectory();
+ } else {
+ base = new File("/mnt/expand/" + volumeUuid);
+ }
+ return new File(base, "user/" + userId + "/" + packageName);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7523675..763a017 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -111,6 +111,9 @@ public class PackageParser {
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+ /** Path prefix for apps on expanded storage */
+ private static final String MNT_EXPAND = "/mnt/expand/";
+
/** @hide */
public static class NewPermissionInfo {
public final String name;
@@ -860,6 +863,12 @@ public class PackageParser {
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
+ String volumeUuid = null;
+ if (apkPath.startsWith(MNT_EXPAND)) {
+ final int end = apkPath.indexOf('/', MNT_EXPAND.length());
+ volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
+ }
+
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
@@ -882,6 +891,7 @@ public class PackageParser {
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
+ pkg.volumeUuid = volumeUuid;
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
@@ -4206,6 +4216,8 @@ public class PackageParser {
// TODO: work towards making these paths invariant
+ public String volumeUuid;
+
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
@@ -4727,7 +4739,8 @@ public class PackageParser {
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
if (userId != 0) {
ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+ ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
+ .getAbsolutePath();
}
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
@@ -4755,7 +4768,8 @@ public class PackageParser {
ai = new ApplicationInfo(ai);
if (userId != 0) {
ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+ ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
+ .getAbsolutePath();
}
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index bfe4aa2..f01c540 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -939,6 +939,129 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<Integer>("android.lens.facing", int.class);
/**
+ * <p>The orientation of the camera relative to the sensor
+ * coordinate system.</p>
+ * <p>The four coefficients that describe the quarternion
+ * rotation from the Android sensor coordinate system to a
+ * camera-aligned coordinate system where the X-axis is
+ * aligned with the long side of the image sensor, the Y-axis
+ * is aligned with the short side of the image sensor, and
+ * the Z-axis is aligned with the optical axis of the sensor.</p>
+ * <p>To convert from the quarternion coefficients <code>(x,y,z,w)</code>
+ * to the axis of rotation <code>(a_x, a_y, a_z)</code> and rotation
+ * amount <code>theta</code>, the following formulas can be used:</p>
+ * <pre><code> theta = 2 * acos(w)
+ * a_x = x / sin(theta/2)
+ * a_y = y / sin(theta/2)
+ * a_z = z / sin(theta/2)
+ * </code></pre>
+ * <p>To create a 3x3 rotation matrix that applies the rotation
+ * defined by this quarternion, the following matrix can be
+ * used:</p>
+ * <pre><code>R = [ 1 - 2y^2 - 2z^2, 2xy - 2zw, 2xz + 2yw,
+ * 2xy + 2zw, 1 - 2x^2 - 2z^2, 2yz - 2xw,
+ * 2xz - 2yw, 2yz + 2xw, 1 - 2x^2 - 2y^2 ]
+ * </code></pre>
+ * <p>This matrix can then be used to apply the rotation to a
+ * column vector point with</p>
+ * <p><code>p' = Rp</code></p>
+ * <p>where <code>p</code> is in the device sensor coordinate system, and
+ * <code>p'</code> is in the camera-oriented coordinate system.</p>
+ * <p><b>Units</b>:
+ * Quarternion coefficients</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_POSE_ROTATION =
+ new Key<float[]>("android.lens.poseRotation", float[].class);
+
+ /**
+ * <p>Position of the camera optical center.</p>
+ * <p>As measured in the device sensor coordinate system, the
+ * position of the camera device's optical center, as a
+ * three-dimensional vector <code>(x,y,z)</code>.</p>
+ * <p>To transform a world position to a camera-device centered
+ * coordinate system, the position must be translated by this
+ * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+ * <p><b>Units</b>: Meters</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_POSE_TRANSLATION =
+ new Key<float[]>("android.lens.poseTranslation", float[].class);
+
+ /**
+ * <p>The parameters for this camera device's intrinsic
+ * calibration.</p>
+ * <p>The five calibration parameters that describe the
+ * transform from camera-centric 3D coordinates to sensor
+ * pixel coordinates:</p>
+ * <pre><code>[f_x, f_y, c_x, c_y, s]
+ * </code></pre>
+ * <p>Where <code>f_x</code> and <code>f_y</code> are the horizontal and vertical
+ * focal lengths, <code>[c_x, c_y]</code> is the position of the optical
+ * axis, and <code>s</code> is a skew parameter for the sensor plane not
+ * being aligned with the lens plane.</p>
+ * <p>These are typically used within a transformation matrix K:</p>
+ * <pre><code>K = [ f_x, s, c_x,
+ * 0, f_y, c_y,
+ * 0 0, 1 ]
+ * </code></pre>
+ * <p>which can then be combined with the camera pose rotation
+ * <code>R</code> and translation <code>t</code> ({@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
+ * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respective) to calculate the
+ * complete transform from world coordinates to pixel
+ * coordinates:</p>
+ * <pre><code>P = [ K 0 * [ R t
+ * 0 1 ] 0 1 ]
+ * </code></pre>
+ * <p>and with <code>p_w</code> being a point in the world coordinate system
+ * and <code>p_s</code> being a point in the camera active pixel array
+ * coordinate system, and with the mapping including the
+ * homogeneous division by z:</p>
+ * <pre><code> p_h = (x_h, y_h, z_h) = P p_w
+ * p_s = p_h / z_h
+ * </code></pre>
+ * <p>so <code>[x_s, y_s]</code> is the pixel coordinates of the world
+ * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity
+ * (depth) in pixel coordinates.</p>
+ * <p><b>Units</b>:
+ * Pixels in the android.sensor.activeArraySize coordinate
+ * system.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_INTRINSIC_CALIBRATION =
+ new Key<float[]>("android.lens.intrinsicCalibration", float[].class);
+
+ /**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial lens distortion.</p>
+ * <p>Three cofficients <code>[kappa_1, kappa_2, kappa_3]</code> that
+ * can be used to correct the lens's radial geometric
+ * distortion with the mapping equations:</p>
+ * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+ * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+ * </code></pre>
+ * <p>where <code>[x_i, y_i]</code> are normalized coordinates with <code>(0,0)</code>
+ * at the lens optical center, and <code>[-1, 1]</code> are the edges of
+ * the active pixel array; and where <code>[x_c, y_c]</code> are the
+ * corrected normalized coordinates with radial distortion
+ * removed; and <code>r^2 = x_i^2 + y_i^2</code>.</p>
+ * <p><b>Units</b>:
+ * Coefficients for a 6th-degree even radial polynomial.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_RADIAL_DISTORTION =
+ new Key<float[]>("android.lens.radialDistortion", float[].class);
+
+ /**
* <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
* by this camera device.</p>
* <p>Full-capability camera devices will always support OFF and FAST.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e346dc2..d8f92e5 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2532,6 +2532,129 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.lens.state", int.class);
/**
+ * <p>The orientation of the camera relative to the sensor
+ * coordinate system.</p>
+ * <p>The four coefficients that describe the quarternion
+ * rotation from the Android sensor coordinate system to a
+ * camera-aligned coordinate system where the X-axis is
+ * aligned with the long side of the image sensor, the Y-axis
+ * is aligned with the short side of the image sensor, and
+ * the Z-axis is aligned with the optical axis of the sensor.</p>
+ * <p>To convert from the quarternion coefficients <code>(x,y,z,w)</code>
+ * to the axis of rotation <code>(a_x, a_y, a_z)</code> and rotation
+ * amount <code>theta</code>, the following formulas can be used:</p>
+ * <pre><code> theta = 2 * acos(w)
+ * a_x = x / sin(theta/2)
+ * a_y = y / sin(theta/2)
+ * a_z = z / sin(theta/2)
+ * </code></pre>
+ * <p>To create a 3x3 rotation matrix that applies the rotation
+ * defined by this quarternion, the following matrix can be
+ * used:</p>
+ * <pre><code>R = [ 1 - 2y^2 - 2z^2, 2xy - 2zw, 2xz + 2yw,
+ * 2xy + 2zw, 1 - 2x^2 - 2z^2, 2yz - 2xw,
+ * 2xz - 2yw, 2yz + 2xw, 1 - 2x^2 - 2y^2 ]
+ * </code></pre>
+ * <p>This matrix can then be used to apply the rotation to a
+ * column vector point with</p>
+ * <p><code>p' = Rp</code></p>
+ * <p>where <code>p</code> is in the device sensor coordinate system, and
+ * <code>p'</code> is in the camera-oriented coordinate system.</p>
+ * <p><b>Units</b>:
+ * Quarternion coefficients</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_POSE_ROTATION =
+ new Key<float[]>("android.lens.poseRotation", float[].class);
+
+ /**
+ * <p>Position of the camera optical center.</p>
+ * <p>As measured in the device sensor coordinate system, the
+ * position of the camera device's optical center, as a
+ * three-dimensional vector <code>(x,y,z)</code>.</p>
+ * <p>To transform a world position to a camera-device centered
+ * coordinate system, the position must be translated by this
+ * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+ * <p><b>Units</b>: Meters</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_POSE_TRANSLATION =
+ new Key<float[]>("android.lens.poseTranslation", float[].class);
+
+ /**
+ * <p>The parameters for this camera device's intrinsic
+ * calibration.</p>
+ * <p>The five calibration parameters that describe the
+ * transform from camera-centric 3D coordinates to sensor
+ * pixel coordinates:</p>
+ * <pre><code>[f_x, f_y, c_x, c_y, s]
+ * </code></pre>
+ * <p>Where <code>f_x</code> and <code>f_y</code> are the horizontal and vertical
+ * focal lengths, <code>[c_x, c_y]</code> is the position of the optical
+ * axis, and <code>s</code> is a skew parameter for the sensor plane not
+ * being aligned with the lens plane.</p>
+ * <p>These are typically used within a transformation matrix K:</p>
+ * <pre><code>K = [ f_x, s, c_x,
+ * 0, f_y, c_y,
+ * 0 0, 1 ]
+ * </code></pre>
+ * <p>which can then be combined with the camera pose rotation
+ * <code>R</code> and translation <code>t</code> ({@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
+ * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respective) to calculate the
+ * complete transform from world coordinates to pixel
+ * coordinates:</p>
+ * <pre><code>P = [ K 0 * [ R t
+ * 0 1 ] 0 1 ]
+ * </code></pre>
+ * <p>and with <code>p_w</code> being a point in the world coordinate system
+ * and <code>p_s</code> being a point in the camera active pixel array
+ * coordinate system, and with the mapping including the
+ * homogeneous division by z:</p>
+ * <pre><code> p_h = (x_h, y_h, z_h) = P p_w
+ * p_s = p_h / z_h
+ * </code></pre>
+ * <p>so <code>[x_s, y_s]</code> is the pixel coordinates of the world
+ * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity
+ * (depth) in pixel coordinates.</p>
+ * <p><b>Units</b>:
+ * Pixels in the android.sensor.activeArraySize coordinate
+ * system.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_INTRINSIC_CALIBRATION =
+ new Key<float[]>("android.lens.intrinsicCalibration", float[].class);
+
+ /**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial lens distortion.</p>
+ * <p>Three cofficients <code>[kappa_1, kappa_2, kappa_3]</code> that
+ * can be used to correct the lens's radial geometric
+ * distortion with the mapping equations:</p>
+ * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+ * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+ * </code></pre>
+ * <p>where <code>[x_i, y_i]</code> are normalized coordinates with <code>(0,0)</code>
+ * at the lens optical center, and <code>[-1, 1]</code> are the edges of
+ * the active pixel array; and where <code>[x_c, y_c]</code> are the
+ * corrected normalized coordinates with radial distortion
+ * removed; and <code>r^2 = x_i^2 + y_i^2</code>.</p>
+ * <p><b>Units</b>:
+ * Coefficients for a 6th-degree even radial polynomial.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ public static final Key<float[]> LENS_RADIAL_DISTORTION =
+ new Key<float[]>("android.lens.radialDistortion", float[].class);
+
+ /**
* <p>Mode of operation for the noise reduction algorithm.</p>
* <p>The noise reduction algorithm attempts to improve image quality by removing
* excessive noise added by the capture process, especially in dark conditions.</p>
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index e3572a2..6b4bc1e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -16,11 +16,14 @@
package android.hardware.fingerprint;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Binder;
import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
@@ -29,6 +32,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
+import android.security.AndroidKeyStoreProvider;
import android.util.Log;
import android.util.Slog;
@@ -41,7 +45,10 @@ import javax.crypto.Cipher;
/**
* A class that coordinates access to the fingerprint hardware.
- * @hide
+ * <p>
+ * Use {@link android.content.Context#getSystemService(java.lang.String)}
+ * with argument {@link android.content.Context#FINGERPRINT_SERVICE} to get
+ * an instance of this class.
*/
public class FingerprintManager {
@@ -53,28 +60,103 @@ public class FingerprintManager {
private static final int MSG_ERROR = 103;
private static final int MSG_REMOVED = 104;
- // Message types. Must agree with HAL (fingerprint.h)
- public static final int FINGERPRINT_ERROR = -1;
- public static final int FINGERPRINT_ACQUIRED = 1;
- public static final int FINGERPRINT_PROCESSED = 2;
- public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
- public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
+ //
+ // Error messages from fingerprint hardware during initilization, enrollment, authentication or
+ // removal. Must agree with the list in fingerprint.h
+ //
- // Error messages. Must agree with HAL (fingerprint.h)
+ /**
+ * The hardware is unavailable. Try again later.
+ */
public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+ /**
+ * The operation was canceled because the fingerprint sensor is unavailable. For example,
+ * this may happen when the user is switched, the device is locked or another pending operation
+ * prevents or disables it.
+ */
public static final int FINGERPRINT_ERROR_CANCELED = 5;
+
+ /**
+ * The {@link FingerprintManager#remove(Fingerprint, RemovalCallback)} call failed. Typically
+ * this will happen when the provided fingerprint id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ */
public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
- // Image acquisition messages. Must agree with HAL (fingerprint.h)
+ //
+ // Image acquisition messages. Must agree with those in fingerprint.h
+ //
+
+ /**
+ * The image acquired was good.
+ */
public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+
+ /**
+ * Only a partial fingerprint image was detected. During enrollment, the user should be
+ * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
+ */
public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+
+ /**
+ * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
+ * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
+ */
public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+
+ /**
+ * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
+ * For example, it's reasonable return this after multiple
+ * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
+ * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
+ * when this is returned.
+ */
public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+
+ /**
+ * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
+ * linear array sensors that require a swipe motion.
+ */
public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+
+ /**
+ * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
+ * linear array sensors, this could also happen if the finger was moved during acquisition.
+ * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
+ * longer.
+ */
public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ */
public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
private IFingerprintService mService;
@@ -85,21 +167,81 @@ public class FingerprintManager {
private RemovalCallback mRemovalCallback;
private CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
- private boolean mListening;
+
+ private class OnEnrollCancelListener implements OnCancelListener {
+ private long mChallenge;
+
+ public OnEnrollCancelListener(long challenge) {
+ mChallenge = challenge;
+ }
+
+ @Override
+ public void onCancel() {
+ cancelEnrollment(mChallenge);
+ }
+ }
+
+ private class OnAuthenticationCancelListener implements OnCancelListener {
+ private CryptoObject mCrypto;
+
+ public OnAuthenticationCancelListener(CryptoObject crypto) {
+ mCrypto = crypto;
+ }
+
+ @Override
+ public void onCancel() {
+ cancelAuthentication(mCrypto);
+ }
+ }
/**
- * A wrapper class for a limited number of crypto objects supported by FingerprintManager.
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature} and {@link Cipher} objects.
*/
public static class CryptoObject {
- CryptoObject(Signature signature) { mSignature = signature; }
- CryptoObject(Cipher cipher) { mCipher = cipher; }
- private Signature mSignature;
- private Cipher mCipher;
+
+ CryptoObject(Signature signature) {
+ mSignature = signature;
+ mCipher = null;
+ }
+
+ CryptoObject(Cipher cipher) {
+ mCipher = cipher;
+ mSignature = null;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() { return mSignature; }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() { return mCipher; }
+
+ /**
+ * @hide
+ * @return the opId associated with this object or 0 if none
+ */
+ public long getOpId() {
+ if (mSignature != null) {
+ return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mSignature);
+ } else if (mCipher != null) {
+ return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCipher);
+ }
+ return 0;
+ }
+
+ private final Signature mSignature;
+ private final Cipher mCipher;
};
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
- * AuthenticationCallback, CancellationSignal, int)}
+ * CancellationSignal, AuthenticationCallback, int)}.
*/
public static final class AuthenticationResult {
private Fingerprint mFingerprint;
@@ -113,14 +255,14 @@ public class FingerprintManager {
/**
* Obtain the crypto object associated with this transaction
* @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
- * AuthenticationCallback, CancellationSignal, int)}
+ * CancellationSignal, AuthenticationCallback, int)}.
*/
public CryptoObject getCryptoObject() { return mCryptoObject; }
/**
- * Obtain the Fingerprint associated with this operation. Applications are discouraged
- * from associating specific fingers with specific applications or operations. Hence this
- * is not public.
+ * Obtain the Fingerprint associated with this operation. Applications are strongly
+ * discouraged from associating specific fingers with specific applications or operations.
+ *
* @hide
*/
public Fingerprint getFingerprint() { return mFingerprint; }
@@ -128,68 +270,75 @@ public class FingerprintManager {
/**
* Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
- * AuthenticationCallback, CancellationSignal, int)}. Users of {@link #FingerprintManager()}
- * must provide an implementation of this to {@link FingerprintManager#authenticate(
- * CryptoObject, AuthenticationCallback, CancellationSignal, int) for listening to fingerprint
- * events.
+ * CancellationSignal, AuthenticationCallback, int)}. Users of {@link
+ * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
+ * AuthenticationCallback, int) } must provide an implementation of this for listening to
+ * fingerprint events.
*/
public static abstract class AuthenticationCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
- * @param errMsgId an integer identifying the error message.
- * @param errString a human-readible error string that can be shown in UI.
+ * @param errMsgId An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
*/
- public abstract void onAuthenticationError(int errMsgId, CharSequence errString);
+ public void onAuthenticationError(int errMsgId, CharSequence errString) { }
/**
- * Called when a recoverable error has been encountered during authentication. The help
+ * Called when a recoverable error has been encountered during authentication. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it."
- * @param helpMsgId an integer identifying the error message.
- * @param helpString a human-readible string that can be shown in UI.
+ * @param helpMsgId An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
*/
- public abstract void onAuthenticationHelp(int helpMsgId, CharSequence helpString);
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
/**
* Called when a fingerprint is recognized.
- * @param result an object containing authentication-related data.
+ * @param result An object containing authentication-related data
*/
- public abstract void onAuthenticationSucceeded(AuthenticationResult result);
+ public void onAuthenticationSucceeded(AuthenticationResult result) { }
+
+ /**
+ * Called when a fingerprint is valid but not recognized.
+ */
+ public void onAuthenticationFailed() { }
};
/**
* Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
* CancellationSignal, int). Users of {@link #FingerprintManager()}
* must provide an implementation of this to {@link FingerprintManager#enroll(long,
- * EnrollmentCallback, CancellationSignal, int) for listening to fingerprint events.
+ * CancellationSignal, EnrollmentCallback, int) for listening to fingerprint events.
+ *
+ * @hide
*/
public static abstract class EnrollmentCallback {
/**
* Called when an unrecoverable error has been encountered and the operation is complete.
* No further callbacks will be made on this object.
- * @param errMsgId an integer identifying the error message.
- * @param errString a human-readible error string that can be shown in UI.
+ * @param errMsgId An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
*/
- public abstract void onEnrollmentError(int errMsgId, CharSequence errString);
+ public void onEnrollmentError(int errMsgId, CharSequence errString) { }
/**
- * Called when a recoverable error has been encountered during enrollment. The help
+ * Called when a recoverable error has been encountered during enrollment. The help
* string is provided to give the user guidance for what went wrong, such as
* "Sensor dirty, please clean it" or what they need to do next, such as
* "Touch sensor again."
- * @param helpMsgId an integer identifying the error message.
- * @param helpString a human-readible string that can be shown in UI.
+ * @param helpMsgId An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
*/
- public abstract void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
+ public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { }
/**
* Called as each enrollment step progresses. Enrollment is considered complete when
- * remaining reaches 0. This function will not be called if enrollment fails. See
+ * remaining reaches 0. This function will not be called if enrollment fails. See
* {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
- * @param remaining the number of remaining steps.
+ * @param remaining The number of remaining steps
*/
- public abstract void onEnrollmentProgress(int remaining);
+ public void onEnrollmentProgress(int remaining) { }
};
/**
@@ -197,53 +346,66 @@ public class FingerprintManager {
* {@link #FingerprintManager()} may optionally provide an implementation of this to
* {@link FingerprintManager#remove(int, int, RemovalCallback)} for listening to
* fingerprint template removal events.
+ *
+ * @hide
*/
public static abstract class RemovalCallback {
/**
* Called when the given fingerprint can't be removed.
- * @param fp the fingerprint that the call attempted to remove.
- * @param errMsgId an associated error message id.
- * @param errString an error message indicating why the fingerprint id can't be removed.
+ * @param fp The fingerprint that the call attempted to remove
+ * @param errMsgId An associated error message id
+ * @param errString An error message indicating why the fingerprint id can't be removed
*/
- public abstract void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString);
+ public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) { }
/**
* Called when a given fingerprint is successfully removed.
* @param fingerprint the fingerprint template that was removed.
*/
- public abstract void onRemovalSucceeded(Fingerprint fingerprint);
+ public void onRemovalSucceeded(Fingerprint fingerprint) { }
};
/**
- * Request authentication of a crypto object. This call warms up the fingerprint hardware
- * and starts scanning for a fingerprint. It terminates when
+ * Request authentication of a crypto object. This call warms up the fingerprint hardware
+ * and starts scanning for a fingerprint. It terminates when
* {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
* {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
*
* @param crypto object associated with the call or null if none required.
- * @param callback an object to receive authentication events
* @param cancel an object that can be used to cancel authentication
- * @param flags optional flags
+ * @param callback an object to receive authentication events
+ * @param flags optional flags; should be 0
*/
- public void authenticate(CryptoObject crypto, AuthenticationCallback callback,
- CancellationSignal cancel, int flags) {
+ public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+ @NonNull AuthenticationCallback callback, int flags) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
- // TODO: handle cancel
+ if (cancel != null) {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "authentication already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+ }
+ }
if (mService != null) try {
mAuthenticationCallback = callback;
mCryptoObject = crypto;
- long sessionId = 0; // TODO: get from crypto object
- startListening();
- mService.authenticate(mToken, sessionId, getCurrentUserId(), flags);
+ long sessionId = crypto != null ? crypto.getOpId() : 0;
+ mService.authenticate(mToken, sessionId, getCurrentUserId(), mServiceReceiver, flags);
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception while authenticating: ", e);
- stopListening();
+ Log.w(TAG, "Remote exception while authenticating: ", e);
+ if (callback != null) {
+ // Though this may not be a hardware issue, it will cause apps to give up or try
+ // again later.
+ callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
+ }
}
}
@@ -257,44 +419,74 @@ public class FingerprintManager {
* provided cancel object.
* @param challenge a unique id provided by a recent verification of device credentials
* (e.g. pin, pattern or password).
- * @param callback an object to receive enrollment events
* @param cancel an object that can be used to cancel enrollment
+ * @param callback an object to receive enrollment events
* @param flags optional flags
+ * @hide
*/
- public void enroll(long challenge, EnrollmentCallback callback,
- CancellationSignal cancel, int flags) {
+ public void enroll(long challenge, CancellationSignal cancel, EnrollmentCallback callback,
+ int flags) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- // TODO: handle cancel
+ if (cancel != null) {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "enrollment already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(challenge));
+ }
+ }
if (mService != null) try {
mEnrollmentCallback = callback;
- startListening();
- mService.enroll(mToken, getCurrentUserId(), flags);
+ mService.enroll(mToken, challenge, getCurrentUserId(), mServiceReceiver, flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception in enroll: ", e);
+ if (callback != null) {
+ // Though this may not be a hardware issue, it will cause apps to give up or try
+ // again later.
+ callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
+ }
+ }
+ }
+
+ /**
+ * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
+ * existing device credentials (e.g. pin/pattern/password).
+ * @hide
+ */
+ public long preEnroll() {
+ long result = 0;
+ if (mService != null) try {
+ result = mService.preEnroll(mToken);
} catch (RemoteException e) {
- Log.v(TAG, "Remote exception in enroll: ", e);
- stopListening();
+ Log.w(TAG, "Remote exception in enroll: ", e);
}
+ return result;
}
/**
* Remove given fingerprint template from fingerprint hardware and/or protected storage.
* @param fp the fingerprint item to remove
* @param callback an optional callback to verify that fingerprint templates have been
- * successfully removed. May be null of no callback is required.
+ * successfully removed. May be null of no callback is required.
+ *
* @hide
*/
public void remove(Fingerprint fp, RemovalCallback callback) {
if (mService != null) try {
mRemovalCallback = callback;
mRemovalFingerprint = fp;
- startListening();
- mService.remove(mToken, fp.getFingerId(), getCurrentUserId());
+ mService.remove(mToken, fp.getFingerId(), getCurrentUserId(), mServiceReceiver);
} catch (RemoteException e) {
- Log.v(TAG, "Remote in remove: ", e);
- stopListening();
+ Log.w(TAG, "Remote exception in remove: ", e);
+ if (callback != null) {
+ callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
+ }
}
}
@@ -302,6 +494,7 @@ public class FingerprintManager {
* Renames the given fingerprint template
* @param fpId the fingerprint id
* @param newName the new name
+ *
* @hide
*/
public void rename(int fpId, String newName) {
@@ -320,6 +513,8 @@ public class FingerprintManager {
/**
* Obtain the list of enrolled fingerprints templates.
* @return list of current fingerprint items
+ *
+ * @hide
*/
public List<Fingerprint> getEnrolledFingerprints() {
if (mService != null) try {
@@ -333,6 +528,7 @@ public class FingerprintManager {
/**
* Determine if fingerprint hardware is present and functional.
* @return true if hardware is present and functional, false otherwise.
+ *
* @hide
*/
public boolean isHardwareDetected() {
@@ -403,8 +599,13 @@ public class FingerprintManager {
private void sendProcessedResult(Fingerprint fp) {
if (mAuthenticationCallback != null) {
- AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
- mAuthenticationCallback.onAuthenticationSucceeded(result);
+ if (fp.getFingerId() == 0 && fp.getGroupId() == 0) {
+ // Fingerprint template valid but doesn't match one in database
+ mAuthenticationCallback.onAuthenticationFailed();
+ } else {
+ final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ }
}
}
@@ -418,65 +619,6 @@ public class FingerprintManager {
mAuthenticationCallback.onAuthenticationHelp(acquireInfo, msg);
}
}
-
- private String getErrorString(int errMsg) {
- switch (errMsg) {
- case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_error_unable_to_process);
- case FINGERPRINT_ERROR_HW_UNAVAILABLE:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_error_hw_not_available);
- case FINGERPRINT_ERROR_NO_SPACE:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_error_no_space);
- case FINGERPRINT_ERROR_TIMEOUT:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_error_timeout);
- default:
- if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) {
- int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE;
- String[] msgArray = mContext.getResources().getStringArray(
- com.android.internal.R.array.fingerprint_error_vendor);
- if (msgNumber < msgArray.length) {
- return msgArray[msgNumber];
- }
- }
- return null;
- }
- }
-
- private String getAcquiredString(int acquireInfo) {
- switch (acquireInfo) {
- case FINGERPRINT_ACQUIRED_GOOD:
- return null;
- case FINGERPRINT_ACQUIRED_PARTIAL:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_acquired_partial);
- case FINGERPRINT_ACQUIRED_INSUFFICIENT:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_acquired_insufficient);
- case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_acquired_imager_dirty);
- case FINGERPRINT_ACQUIRED_TOO_SLOW:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_acquired_too_slow);
- case FINGERPRINT_ACQUIRED_TOO_FAST:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_acquired_too_fast);
- default:
- if (acquireInfo >= FINGERPRINT_ACQUIRED_VENDOR_BASE) {
- int msgNumber = acquireInfo - FINGERPRINT_ACQUIRED_VENDOR_BASE;
- String[] msgArray = mContext.getResources().getStringArray(
- com.android.internal.R.array.fingerprint_acquired_vendor);
- if (msgNumber < msgArray.length) {
- return msgArray[msgNumber];
- }
- }
- return null;
- }
- }
};
/**
@@ -499,39 +641,84 @@ public class FingerprintManager {
}
}
- /**
- * Stops the client from listening to fingerprint events.
- */
- private void stopListening() {
- if (mService != null) {
- try {
- if (mListening) {
- mService.removeListener(mToken, mServiceReceiver);
- mListening = false;
+ private void clearCallbacks() {
+ mAuthenticationCallback = null;
+ mEnrollmentCallback = null;
+ mRemovalCallback = null;
+ }
+
+ private void cancelEnrollment(long challenge) {
+ if (mService != null) try {
+ mService.cancelEnrollment(mToken);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment");
+ }
+ }
+
+ private void cancelAuthentication(CryptoObject cryptoObject) {
+ if (mService != null) try {
+ mService.cancelAuthentication(mToken);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment");
+ }
+ }
+
+ private String getErrorString(int errMsg) {
+ switch (errMsg) {
+ case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_unable_to_process);
+ case FINGERPRINT_ERROR_HW_UNAVAILABLE:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_hw_not_available);
+ case FINGERPRINT_ERROR_NO_SPACE:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_no_space);
+ case FINGERPRINT_ERROR_TIMEOUT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_error_timeout);
+ default:
+ if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) {
+ int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE;
+ String[] msgArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.fingerprint_error_vendor);
+ if (msgNumber < msgArray.length) {
+ return msgArray[msgNumber];
+ }
}
- } catch (RemoteException e) {
- Log.v(TAG, "Remote exception in stopListening(): ", e);
- }
- } else {
- Log.w(TAG, "stopListening(): Service not connected!");
+ return null;
}
}
- /**
- * Starts listening for fingerprint events for this client.
- */
- private void startListening() {
- if (mService != null) {
- try {
- if (!mListening) {
- mService.addListener(mToken, mServiceReceiver, getCurrentUserId());
- mListening = true;
+ private String getAcquiredString(int acquireInfo) {
+ switch (acquireInfo) {
+ case FINGERPRINT_ACQUIRED_GOOD:
+ return null;
+ case FINGERPRINT_ACQUIRED_PARTIAL:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_partial);
+ case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_insufficient);
+ case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_imager_dirty);
+ case FINGERPRINT_ACQUIRED_TOO_SLOW:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_too_slow);
+ case FINGERPRINT_ACQUIRED_TOO_FAST:
+ return mContext.getString(
+ com.android.internal.R.string.fingerprint_acquired_too_fast);
+ default:
+ if (acquireInfo >= FINGERPRINT_ACQUIRED_VENDOR_BASE) {
+ int msgNumber = acquireInfo - FINGERPRINT_ACQUIRED_VENDOR_BASE;
+ String[] msgArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.fingerprint_acquired_vendor);
+ if (msgNumber < msgArray.length) {
+ return msgArray[msgNumber];
+ }
}
- } catch (RemoteException e) {
- Log.v(TAG, "Remote exception in startListening(): ", e);
- }
- } else {
- Log.w(TAG, "startListening(): Service not connected!");
+ return null;
}
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index c5a45e2..2fcb20e 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -26,13 +26,21 @@ import java.util.List;
*/
interface IFingerprintService {
// Authenticate the given sessionId with a fingerprint
- void authenticate(IBinder token, long sessionId, int groupId, int flags);
+ void authenticate(IBinder token, long sessionId, int groupId,
+ IFingerprintServiceReceiver receiver, int flags);
+
+ // Cancel authentication for the given sessionId
+ void cancelAuthentication(IBinder token);
// Start fingerprint enrollment
- void enroll(IBinder token, int groupId, int flags);
+ void enroll(IBinder token, long challenge, int groupId, IFingerprintServiceReceiver receiver,
+ int flags);
+
+ // Cancel enrollment in progress
+ void cancelEnrollment(IBinder token);
// Any errors resulting from this call will be returned to the listener
- void remove(IBinder token, int fingerId, int groupId);
+ void remove(IBinder token, int fingerId, int groupId, IFingerprintServiceReceiver receiver);
// Rename the fingerprint specified by fingerId and groupId to the given name
void rename(int fingerId, int groupId, String name);
@@ -40,15 +48,12 @@ interface IFingerprintService {
// Get a list of enrolled fingerprints in the given group.
List<Fingerprint> getEnrolledFingerprints(int groupId);
- // Register listener for an instance of FingerprintManager
- void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId);
-
- // Unregister listener for an instance of FingerprintManager
- void removeListener(IBinder token, IFingerprintServiceReceiver receiver);
-
// Determine if HAL is loaded and ready
boolean isHardwareDetected(long deviceId);
+ // Get a pre-enrollment authentication token
+ long preEnroll(IBinder token);
+
// Gets the number of hardware devices
// int getHardwareDeviceCount();
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3abccbc..da2c5e0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -589,9 +589,9 @@ public class ConnectivityManager {
* network.
*
* @return a {@link NetworkInfo} object for the current default network
- * or {@code null} if no network default network is currently active
+ * or {@code null} if no default network is currently active
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
*/
public NetworkInfo getActiveNetworkInfo() {
@@ -738,9 +738,9 @@ public class ConnectivityManager {
* network.
*
* @return a {@link NetworkInfo} object for the current default network
- * or {@code null} if no network default network is currently active
+ * or {@code null} if no default network is currently active
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
*
* {@hide}
@@ -760,7 +760,7 @@ public class ConnectivityManager {
* for the current default network, or {@code null} if there
* is no current default network.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -780,7 +780,7 @@ public class ConnectivityManager {
* for the given networkType, or {@code null} if there is
* no current default network.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -830,7 +830,7 @@ public class ConnectivityManager {
* @return a boolean, {@code true} indicating success. All network types
* will be tried, even if some fail.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
@@ -851,7 +851,7 @@ public class ConnectivityManager {
* {@code} false to turn it off.
* @return a boolean, {@code true} indicating success.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
@@ -1202,7 +1202,7 @@ public class ConnectivityManager {
* @return {@code true} on success, {@code false} on failure
*
* @deprecated Deprecated in favor of the {@link #requestNetwork},
- * {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api.
+ * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1220,7 +1220,7 @@ public class ConnectivityManager {
* @return {@code true} on success, {@code false} on failure
* @hide
* @deprecated Deprecated in favor of the {@link #requestNetwork} and
- * {@link #setProcessDefaultNetwork} api.
+ * {@link #bindProcessToNetwork} api.
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
try {
@@ -1273,7 +1273,7 @@ public class ConnectivityManager {
* network is active. Quota status can change rapidly, so these values
* shouldn't be cached.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
*
* @hide
@@ -1345,7 +1345,7 @@ public class ConnectivityManager {
* listener.
* <p>
* If the process default network has been set with
- * {@link ConnectivityManager#setProcessDefaultNetwork} this function will not
+ * {@link ConnectivityManager#bindProcessToNetwork} this function will not
* reflect the process's default, but the system default.
*
* @param l The listener to be told when the network is active.
@@ -1430,11 +1430,20 @@ public class ConnectivityManager {
* situations where a Context pointer is unavailable.
* @hide
*/
- public static ConnectivityManager getInstance() {
- if (sInstance == null) {
+ static ConnectivityManager getInstanceOrNull() {
+ return sInstance;
+ }
+
+ /**
+ * @deprecated - use getSystemService. This is a kludge to support static access in certain
+ * situations where a Context pointer is unavailable.
+ * @hide
+ */
+ private static ConnectivityManager getInstance() {
+ if (getInstanceOrNull() == null) {
throw new IllegalStateException("No ConnectivityManager yet constructed");
}
- return sInstance;
+ return getInstanceOrNull();
}
/**
@@ -1443,7 +1452,7 @@ public class ConnectivityManager {
*
* @return an array of 0 or more Strings of tetherable interface names.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1460,7 +1469,7 @@ public class ConnectivityManager {
*
* @return an array of 0 or more String of currently tethered interface names.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1483,7 +1492,7 @@ public class ConnectivityManager {
* @return an array of 0 or more String indicating the interface names
* which failed to tether.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1521,7 +1530,7 @@ public class ConnectivityManager {
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
@@ -1539,7 +1548,7 @@ public class ConnectivityManager {
* @param iface the interface name to untether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
@@ -1558,7 +1567,7 @@ public class ConnectivityManager {
*
* @return a boolean - {@code true} indicating Tethering is supported.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1578,7 +1587,7 @@ public class ConnectivityManager {
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable usb interfaces.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1598,7 +1607,7 @@ public class ConnectivityManager {
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable wifi interfaces.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1618,7 +1627,7 @@ public class ConnectivityManager {
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable bluetooth interfaces.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1640,7 +1649,7 @@ public class ConnectivityManager {
* @param enable a boolean - {@code true} to enable tethering
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
* {@hide}
*/
@@ -1683,7 +1692,7 @@ public class ConnectivityManager {
* @return error The error code of the last error tethering or untethering the named
* interface
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* {@hide}
*/
@@ -1702,7 +1711,7 @@ public class ConnectivityManager {
* @param networkType The type of network you want to report on
* @param percentage The quality of the connection 0 is bad, 100 is good
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#STATUS_BAR}.
* {@hide}
*/
@@ -1738,7 +1747,7 @@ public class ConnectivityManager {
* @param p The a {@link ProxyInfo} object defining the new global
* HTTP proxy. A {@code null} value will clear the global HTTP proxy.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* android.Manifest.permission#CONNECTIVITY_INTERNAL.
* @hide
*/
@@ -1755,7 +1764,7 @@ public class ConnectivityManager {
* @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
* if no global HTTP proxy is set.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* @hide
*/
@@ -1770,15 +1779,14 @@ public class ConnectivityManager {
/**
* Get the current default HTTP proxy settings. If a global proxy is set it will be returned,
* otherwise if this process is bound to a {@link Network} using
- * {@link #setProcessDefaultNetwork} then that {@code Network}'s proxy is returned, otherwise
+ * {@link #bindProcessToNetwork} then that {@code Network}'s proxy is returned, otherwise
* the default network's proxy is returned.
*
* @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
* HTTP proxy is active.
- * @hide
*/
public ProxyInfo getDefaultProxy() {
- final Network network = getProcessDefaultNetwork();
+ final Network network = getBoundNetworkForProcess();
if (network != null) {
final ProxyInfo globalProxy = getGlobalProxy();
if (globalProxy != null) return globalProxy;
@@ -1804,7 +1812,7 @@ public class ConnectivityManager {
* @param networkType The network type we'd like to check
* @return {@code true} if supported, else {@code false}
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
* @hide
*/
@@ -1826,7 +1834,7 @@ public class ConnectivityManager {
* @return {@code true} if large transfers should be avoided, otherwise
* {@code false}.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
*/
public boolean isActiveNetworkMetered() {
@@ -1862,7 +1870,7 @@ public class ConnectivityManager {
* in question.
* @param isCaptivePortal true/false.
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
* {@hide}
*/
@@ -1937,7 +1945,7 @@ public class ConnectivityManager {
*
* @param enable whether to enable airplane mode or not
*
- * <p>This method requires the call to hold the permission
+ * <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
* @hide
*/
@@ -2338,9 +2346,8 @@ public class ConnectivityManager {
* successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
* <p>
- * Note that if you intend to invoke {@link #setProcessDefaultNetwork} or
- * {@link Network#openConnection(java.net.URL)} then you must get a
- * ConnectivityManager instance before doing so.
+ * Note that if you intend to invoke {@link Network#openConnection(java.net.URL)}
+ * then you must get a ConnectivityManager instance before doing so.
*/
public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -2491,15 +2498,42 @@ public class ConnectivityManager {
* Sockets created by Network.getSocketFactory().createSocket() and
* performing network-specific host name resolutions via
* {@link Network#getAllByName Network.getAllByName} is preferred to calling
+ * {@code bindProcessToNetwork}.
+ *
+ * @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 boolean bindProcessToNetwork(Network network) {
+ // Forcing callers to call thru non-static function ensures ConnectivityManager
+ // instantiated.
+ return setProcessDefaultNetwork(network);
+ }
+
+ /**
+ * 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.
+ * @deprecated This function can throw {@link IllegalStateException}. Use
+ * {@link #bindProcessToNetwork} instead. {@code bindProcessToNetwork}
+ * is a direct replacement.
*/
public static boolean setProcessDefaultNetwork(Network network) {
int netId = (network == null) ? NETID_UNSET : network.netId;
- if (netId == NetworkUtils.getNetworkBoundToProcess()) {
+ if (netId == NetworkUtils.getBoundNetworkForProcess()) {
return true;
}
if (NetworkUtils.bindProcessToNetwork(netId)) {
@@ -2519,19 +2553,34 @@ public class ConnectivityManager {
/**
* Returns the {@link Network} currently bound to this process via
- * {@link #setProcessDefaultNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+ * {@link #bindProcessToNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+ *
+ * @return {@code Network} to which this process is bound, or {@code null}.
+ */
+ public Network getBoundNetworkForProcess() {
+ // Forcing callers to call thru non-static function ensures ConnectivityManager
+ // instantiated.
+ return getProcessDefaultNetwork();
+ }
+
+ /**
+ * Returns the {@link Network} currently bound to this process via
+ * {@link #bindProcessToNetwork}, or {@code null} if no {@link Network} is explicitly bound.
*
* @return {@code Network} to which this process is bound, or {@code null}.
+ * @deprecated Using this function can lead to other functions throwing
+ * {@link IllegalStateException}. Use {@link #getBoundNetworkForProcess} instead.
+ * {@code getBoundNetworkForProcess} is a direct replacement.
*/
public static Network getProcessDefaultNetwork() {
- int netId = NetworkUtils.getNetworkBoundToProcess();
+ int netId = NetworkUtils.getBoundNetworkForProcess();
if (netId == NETID_UNSET) return null;
return new Network(netId);
}
/**
* Binds host resolutions performed by this process to {@code network}.
- * {@link #setProcessDefaultNetwork} takes precedence over this setting.
+ * {@link #bindProcessToNetwork} 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.
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 1b8adc8..73ef78e 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -37,7 +37,7 @@ import android.util.Log;
* StateMachine that interacts with the native DHCP client and can talk to
* a controller that also needs to be a StateMachine
*
- * The Dhcp state machine provides the following features:
+ * The DhcpStateMachine provides the following features:
* - Wakeup and renewal using the native DHCP client (which will not renew
* on its own when the device is in suspend state and this can lead to device
* holding IP address beyond expiry)
@@ -72,11 +72,6 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
//Used for sanity check on setting up renewal
private static final int MIN_RENEWAL_TIME_SECS = 5 * 60; // 5 minutes
- private enum DhcpAction {
- START,
- RENEW
- };
-
private final String mInterfaceName;
private boolean mRegisteredForPreDhcpNotification = false;
@@ -99,6 +94,9 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
* after pre DHCP action is complete */
public static final int CMD_PRE_DHCP_ACTION_COMPLETE = BASE + 7;
+ /* Command from ourselves to see if DHCP results are available */
+ private static final int CMD_GET_DHCP_RESULTS = BASE + 8;
+
/* Message.arg1 arguments to CMD_POST_DHCP notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
@@ -108,6 +106,7 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
private State mWaitBeforeStartState = new WaitBeforeStartState();
private State mRunningState = new RunningState();
private State mWaitBeforeRenewalState = new WaitBeforeRenewalState();
+ private State mPollingState = new PollingState();
private DhcpStateMachine(Context context, StateMachine controller, String intf) {
super(TAG);
@@ -139,6 +138,7 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
addState(mDefaultState);
addState(mStoppedState, mDefaultState);
addState(mWaitBeforeStartState, mDefaultState);
+ addState(mPollingState, mDefaultState);
addState(mRunningState, mDefaultState);
addState(mWaitBeforeRenewalState, mDefaultState);
@@ -206,6 +206,10 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
@Override
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
+ if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+ Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
+ }
+ mDhcpResults = null;
}
@Override
@@ -219,7 +223,7 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
mController.sendMessage(CMD_PRE_DHCP_ACTION);
transitionTo(mWaitBeforeStartState);
} else {
- if (runDhcp(DhcpAction.START)) {
+ if (runDhcpStart()) {
transitionTo(mRunningState);
}
}
@@ -247,10 +251,10 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_PRE_DHCP_ACTION_COMPLETE:
- if (runDhcp(DhcpAction.START)) {
+ if (runDhcpStart()) {
transitionTo(mRunningState);
} else {
- transitionTo(mStoppedState);
+ transitionTo(mPollingState);
}
break;
case CMD_STOP_DHCP:
@@ -267,6 +271,55 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
}
}
+ class PollingState extends State {
+ private static final long MAX_DELAY_SECONDS = 32;
+ private long delaySeconds;
+
+ private void scheduleNextResultsCheck() {
+ sendMessageDelayed(obtainMessage(CMD_GET_DHCP_RESULTS), delaySeconds * 1000);
+ delaySeconds *= 2;
+ if (delaySeconds > MAX_DELAY_SECONDS) {
+ delaySeconds = MAX_DELAY_SECONDS;
+ }
+ }
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, "Entering " + getName() + "\n");
+ delaySeconds = 1;
+ scheduleNextResultsCheck();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ boolean retValue = HANDLED;
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_GET_DHCP_RESULTS:
+ if (DBG) Log.d(TAG, "GET_DHCP_RESULTS on " + mInterfaceName);
+ if (dhcpSucceeded()) {
+ transitionTo(mRunningState);
+ } else {
+ scheduleNextResultsCheck();
+ }
+ break;
+ case CMD_STOP_DHCP:
+ transitionTo(mStoppedState);
+ break;
+ default:
+ retValue = NOT_HANDLED;
+ break;
+ }
+ return retValue;
+ }
+
+ @Override
+ public void exit() {
+ if (DBG) Log.d(TAG, "Exiting " + getName() + "\n");
+ removeMessages(CMD_GET_DHCP_RESULTS);
+ }
+ }
+
class RunningState extends State {
@Override
public void enter() {
@@ -280,9 +333,6 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
switch (message.what) {
case CMD_STOP_DHCP:
mAlarmManager.cancel(mDhcpRenewalIntent);
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
- }
transitionTo(mStoppedState);
break;
case CMD_RENEW_DHCP:
@@ -292,7 +342,7 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
transitionTo(mWaitBeforeRenewalState);
//mDhcpRenewWakeLock is released in WaitBeforeRenewalState
} else {
- if (!runDhcp(DhcpAction.RENEW)) {
+ if (!runDhcpRenew()) {
transitionTo(mStoppedState);
}
mDhcpRenewWakeLock.release();
@@ -321,13 +371,10 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
switch (message.what) {
case CMD_STOP_DHCP:
mAlarmManager.cancel(mDhcpRenewalIntent);
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
- }
transitionTo(mStoppedState);
break;
case CMD_PRE_DHCP_ACTION_COMPLETE:
- if (runDhcp(DhcpAction.RENEW)) {
+ if (runDhcpRenew()) {
transitionTo(mRunningState);
} else {
transitionTo(mStoppedState);
@@ -348,52 +395,68 @@ public class DhcpStateMachine extends BaseDhcpStateMachine {
}
}
- private boolean runDhcp(DhcpAction dhcpAction) {
- boolean success = false;
+ private boolean dhcpSucceeded() {
DhcpResults dhcpResults = new DhcpResults();
-
- if (dhcpAction == DhcpAction.START) {
- /* Stop any existing DHCP daemon before starting new */
- NetworkUtils.stopDhcp(mInterfaceName);
- if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
- success = NetworkUtils.runDhcp(mInterfaceName, dhcpResults);
- } else if (dhcpAction == DhcpAction.RENEW) {
- if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
- success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpResults);
- if (success) dhcpResults.updateFromDhcpRequest(mDhcpResults);
+ if (!NetworkUtils.getDhcpResults(mInterfaceName, dhcpResults)) {
+ return false;
}
- if (success) {
- if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
- long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
-
- //Sanity check for renewal
- if (leaseDuration >= 0) {
- //TODO: would be good to notify the user that his network configuration is
- //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
- if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
- leaseDuration = MIN_RENEWAL_TIME_SECS;
- }
- //Do it a bit earlier than half the lease duration time
- //to beat the native DHCP client and avoid extra packets
- //48% for one hour lease time = 29 minutes
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() +
- leaseDuration * 480, //in milliseconds
- mDhcpRenewalIntent);
- } else {
- //infinite lease time, no renewal needed
- }
- mDhcpResults = dhcpResults;
- mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
- .sendToTarget();
+ if (DBG) Log.d(TAG, "DHCP results found for " + mInterfaceName);
+ long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
+
+ //Sanity check for renewal
+ if (leaseDuration >= 0) {
+ //TODO: would be good to notify the user that his network configuration is
+ //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
+ if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
+ leaseDuration = MIN_RENEWAL_TIME_SECS;
+ }
+ //Do it a bit earlier than half the lease duration time
+ //to beat the native DHCP client and avoid extra packets
+ //48% for one hour lease time = 29 minutes
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() +
+ leaseDuration * 480, //in milliseconds
+ mDhcpRenewalIntent);
} else {
- Log.e(TAG, "DHCP failed on " + mInterfaceName + ": " +
+ //infinite lease time, no renewal needed
+ }
+
+ // Fill in any missing fields in dhcpResults from the previous results.
+ // If mDhcpResults is null (i.e. this is the first server response),
+ // this is a noop.
+ dhcpResults.updateFromDhcpRequest(mDhcpResults);
+ mDhcpResults = dhcpResults;
+ mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
+ .sendToTarget();
+ return true;
+ }
+
+ private boolean runDhcpStart() {
+ /* Stop any existing DHCP daemon before starting new */
+ NetworkUtils.stopDhcp(mInterfaceName);
+ mDhcpResults = null;
+
+ if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
+ if (!NetworkUtils.startDhcp(mInterfaceName) || !dhcpSucceeded()) {
+ Log.e(TAG, "DHCP request failed on " + mInterfaceName + ": " +
+ NetworkUtils.getDhcpError());
+ mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
+ .sendToTarget();
+ return false;
+ }
+ return true;
+ }
+
+ private boolean runDhcpRenew() {
+ if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
+ if (!NetworkUtils.startDhcpRenew(mInterfaceName) || !dhcpSucceeded()) {
+ Log.e(TAG, "DHCP renew failed on " + mInterfaceName + ": " +
NetworkUtils.getDhcpError());
- NetworkUtils.stopDhcp(mInterfaceName);
mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
- .sendToTarget();
+ .sendToTarget();
+ return false;
}
- return success;
+ return true;
}
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index ab57c9b..65d325a1 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -48,7 +48,7 @@ import com.android.okhttp.internal.Internal;
* {@link ConnectivityManager#registerNetworkCallback} calls.
* 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 ConnectivityManager#setProcessDefaultNetwork}.
+ * {@link ConnectivityManager#bindProcessToNetwork}.
*/
public class Network implements Parcelable {
@@ -242,7 +242,10 @@ public class Network implements Parcelable {
* @see java.net.URL#openConnection()
*/
public URLConnection openConnection(URL url) throws IOException {
- final ConnectivityManager cm = ConnectivityManager.getInstance();
+ final ConnectivityManager cm = ConnectivityManager.getInstanceOrNull();
+ if (cm == null) {
+ throw new IOException("No ConnectivityManager yet constructed, please construct one");
+ }
// TODO: Should this be optimized to avoid fetching the global proxy for every request?
ProxyInfo proxyInfo = cm.getGlobalProxy();
if (proxyInfo == null) {
@@ -269,7 +272,6 @@ public class Network implements Parcelable {
* @throws IllegalArgumentException if the argument proxy is null.
* @throws IOException if an error occurs while opening the connection.
* @see java.net.URL#openConnection()
- * @hide
*/
public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException {
if (proxy == null) throw new IllegalArgumentException("proxy is null");
@@ -299,7 +301,7 @@ public class Network implements Parcelable {
/**
* Binds the specified {@link DatagramSocket} to this {@code Network}. All data traffic on the
* socket will be sent on this {@code Network}, irrespective of any process-wide network binding
- * set by {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be
+ * set by {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be
* connected.
*/
public void bindSocket(DatagramSocket socket) throws IOException {
@@ -316,7 +318,7 @@ public class Network implements Parcelable {
/**
* Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket
* will be sent on this {@code Network}, irrespective of any process-wide network binding set by
- * {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be connected.
+ * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
*/
public void bindSocket(Socket socket) throws IOException {
// Apparently, the kernel doesn't update a connected TCP socket's routing upon mark changes.
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 02fbe73..29dd8ad 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -159,7 +159,7 @@ public class NetworkUtils {
* Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
* {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
*/
- public native static int getNetworkBoundToProcess();
+ public native static int getBoundNetworkForProcess();
/**
* Binds host resolutions performed by this process to the network designated by {@code netId}.
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 17a84a7..7172c09 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -40,14 +40,9 @@ public final class Proxy {
private static final ProxySelector sDefaultProxySelector;
/**
- * Used to notify an app that's caching the default connection proxy
- * that either the default connection or its proxy has changed.
- * The intent will have the following extra value:</p>
- * <ul>
- * <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy. Non-null,
- * though if the proxy is undefined the host string
- * will be empty.
- * </ul>
+ * Used to notify an app that's caching the proxy that either the default
+ * connection has changed or any connection's proxy has changed. The new
+ * proxy should be queried using {@link ConnectivityManager#getDefaultProxy()}.
*
* <p class="note">This is a protected intent that can only be sent by the system
*/
@@ -56,6 +51,11 @@ public final class Proxy {
/**
* Intent extra included with {@link #PROXY_CHANGE_ACTION} intents.
* It describes the new proxy being used (as a {@link ProxyInfo} object).
+ * @deprecated Because {@code PROXY_CHANGE_ACTION} is sent whenever the proxy
+ * for any network on the system changes, applications should always use
+ * {@link ConnectivityManager#getDefaultProxy()} or
+ * {@link ConnectivityManager#getLinkProperties(Network)}.{@link LinkProperties#getHttpProxy()}
+ * to get the proxy for the Network(s) they are using.
*/
public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index c26af06..a0e65eb 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -679,7 +679,7 @@ public class VpnService extends Service {
*
* By default, all traffic from apps is forwarded through the VPN interface and it is not
* possible for apps to side-step the VPN. If this method is called, apps may use methods
- * such as {@link ConnectivityManager#setProcessDefaultNetwork} to instead send/receive
+ * such as {@link ConnectivityManager#bindProcessToNetwork} to instead send/receive
* directly over the underlying network or any other network they have permissions for.
*
* @return this {@link Builder} object to facilitate chaining of method calls.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1566985..c7edb1a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1947,6 +1947,13 @@ public abstract class BatteryStats implements Parcelable {
public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_POWER_DRAIN + 1;
/**
+ * Returns true if the BatteryStats object has detailed bluetooth power reports.
+ * When true, calling {@link #getBluetoothControllerActivity(int, int)} will yield the
+ * actual power data.
+ */
+ public abstract boolean hasBluetoothActivityReporting();
+
+ /**
* For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
* {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
* respective state.
@@ -1956,6 +1963,13 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getBluetoothControllerActivity(int type, int which);
/**
+ * Returns true if the BatteryStats object has detailed WiFi power reports.
+ * When true, calling {@link #getWifiControllerActivity(int, int)} will yield the
+ * actual power data.
+ */
+ public abstract boolean hasWifiActivityReporting();
+
+ /**
* For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
* {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
* respective state.
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
deleted file mode 100644
index 38abfc0..0000000
--- a/core/java/android/os/IHardwareService.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2007, 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.os;
-
-/** {@hide} */
-interface IHardwareService
-{
- // obsolete flashlight support
- boolean getFlashlightEnabled();
- void setFlashlightEnabled(boolean on);
-}
-
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 236003b..c2fd3c3 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -38,6 +38,7 @@ interface IUserManager {
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
UserInfo getProfileParent(int userHandle);
UserInfo getUserInfo(int userHandle);
+ long getUserCreationTime(int userHandle);
boolean isRestricted();
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3601a1c..b9e307f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1320,4 +1320,19 @@ public class UserManager {
}
return new Bundle();
}
+
+ /**
+ * Returns creation time of the user or of a managed profile associated with the calling user.
+ * @param userHandle user handle of the user or a managed profile associated with the
+ * calling user.
+ * @return creation time in milliseconds since Epoch time.
+ */
+ public long getUserCreationTime(UserHandle userHandle) {
+ try {
+ return mService.getUserCreationTime(userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user creation time", re);
+ return 0;
+ }
+ }
}
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 56c55f1..4704b67 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -16,7 +16,7 @@
package android.os.storage;
-import android.content.Context;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DebugUtils;
@@ -57,12 +57,12 @@ public class DiskInfo implements Parcelable {
volumes = parcel.readStringArray();
}
- public String getDescription(Context context) {
+ public String getDescription() {
// TODO: splice vendor label into these strings
if ((flags & FLAG_SD) != 0) {
- return context.getString(com.android.internal.R.string.storage_sd_card);
+ return Resources.getSystem().getString(com.android.internal.R.string.storage_sd_card);
} else if ((flags & FLAG_USB) != 0) {
- return context.getString(com.android.internal.R.string.storage_usb);
+ return Resources.getSystem().getString(com.android.internal.R.string.storage_usb);
} else {
return null;
}
@@ -119,7 +119,7 @@ public class DiskInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(id);
- parcel.writeInt(flags);
+ parcel.writeInt(this.flags);
parcel.writeLong(size);
parcel.writeString(label);
parcel.writeStringArray(volumes);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5a5d8b7..bd42f6a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -30,10 +30,12 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -454,6 +456,18 @@ public class StorageManager {
}
/** {@hide} */
+ public @Nullable DiskInfo findDiskByVolumeId(String volId) {
+ Preconditions.checkNotNull(volId);
+ // TODO; go directly to service to make this faster
+ for (DiskInfo disk : getDisks()) {
+ if (ArrayUtils.contains(disk.volumes, volId)) {
+ return disk;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public @Nullable VolumeInfo findVolumeById(String id) {
Preconditions.checkNotNull(id);
// TODO; go directly to service to make this faster
@@ -487,6 +501,23 @@ public class StorageManager {
}
/** {@hide} */
+ public @Nullable String getBestVolumeDescription(String volId) {
+ String descrip = null;
+
+ final VolumeInfo vol = findVolumeById(volId);
+ if (vol != null) {
+ descrip = vol.getDescription();
+ }
+
+ final DiskInfo disk = findDiskByVolumeId(volId);
+ if (disk != null && TextUtils.isEmpty(descrip)) {
+ descrip = disk.getDescription();
+ }
+
+ return descrip;
+ }
+
+ /** {@hide} */
public void mount(String volId) {
try {
mMountService.mount(volId);
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 2dc0361..beca8b8 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.mtp.MtpStorage;
import android.os.Environment;
import android.os.Parcel;
@@ -44,6 +45,8 @@ import java.io.File;
* @hide
*/
public class VolumeInfo implements Parcelable {
+ /** Stub volume representing internal private storage */
+ public static final String ID_PRIVATE_INTERNAL = "private";
/** Real volume representing internal emulated storage */
public static final String ID_EMULATED_INTERNAL = "emulated";
@@ -59,6 +62,7 @@ public class VolumeInfo implements Parcelable {
public static final int STATE_FORMATTING = 3;
public static final int STATE_UNMOUNTING = 4;
public static final int STATE_UNMOUNTABLE = 5;
+ public static final int STATE_REMOVED = 6;
public static final int FLAG_PRIMARY = 1 << 0;
public static final int FLAG_VISIBLE = 1 << 1;
@@ -73,12 +77,14 @@ public class VolumeInfo implements Parcelable {
sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTING, Environment.MEDIA_EJECTING);
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE);
+ sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
+ sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
}
/** vold state */
@@ -96,8 +102,6 @@ public class VolumeInfo implements Parcelable {
public final int mtpIndex;
public String nickname;
- public DiskInfo disk;
-
public VolumeInfo(String id, int type, int mtpIndex) {
this.id = Preconditions.checkNotNull(id);
this.type = type;
@@ -135,9 +139,9 @@ public class VolumeInfo implements Parcelable {
return getBroadcastForEnvironment(getEnvironmentForState(state));
}
- public String getDescription(Context context) {
- if (ID_EMULATED_INTERNAL.equals(id)) {
- return context.getString(com.android.internal.R.string.storage_internal);
+ public @Nullable String getDescription() {
+ if (ID_PRIVATE_INTERNAL.equals(id)) {
+ return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
} else if (!TextUtils.isEmpty(nickname)) {
return nickname;
} else if (!TextUtils.isEmpty(fsLabel)) {
@@ -189,7 +193,7 @@ public class VolumeInfo implements Parcelable {
userPath = new File("/dev/null");
}
- String description = getDescription(context);
+ String description = getDescription();
if (description == null) {
description = context.getString(android.R.string.unknownName);
}
@@ -283,7 +287,7 @@ public class VolumeInfo implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(id);
parcel.writeInt(type);
- parcel.writeInt(flags);
+ parcel.writeInt(this.flags);
parcel.writeInt(userId);
parcel.writeInt(state);
parcel.writeString(fsType);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 9a0858a..69338b0 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -106,6 +106,14 @@ public final class DocumentsContract {
/** {@hide} */
public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
+ /** {@hide} */
+ public static final String
+ ACTION_BROWSE_DOCUMENT_ROOT = "android.provider.action.BROWSE_DOCUMENT_ROOT";
+
+ /** {@hide} */
+ public static final String
+ ACTION_DOCUMENT_ROOT_SETTINGS = "android.provider.action.DOCUMENT_ROOT_SETTINGS";
+
/**
* Buffer is large enough to rewind past any EXIF headers.
*/
@@ -473,6 +481,15 @@ public final class DocumentsContract {
* @hide
*/
public static final int FLAG_ADVANCED = 1 << 17;
+
+ /**
+ * Flag indicating that this root has settings.
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#ACTION_DOCUMENT_ROOT_SETTINGS
+ * @hide
+ */
+ public static final int FLAG_HAS_SETTINGS = 1 << 18;
}
/**
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index 0f1d422..b3a3aad 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -19,6 +19,10 @@ package android.security.keymaster;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
/**
* @hide
*/
@@ -28,10 +32,12 @@ public class KeyCharacteristics implements Parcelable {
public static final Parcelable.Creator<KeyCharacteristics> CREATOR = new
Parcelable.Creator<KeyCharacteristics>() {
+ @Override
public KeyCharacteristics createFromParcel(Parcel in) {
return new KeyCharacteristics(in);
}
+ @Override
public KeyCharacteristics[] newArray(int length) {
return new KeyCharacteristics[length];
}
@@ -48,6 +54,7 @@ public class KeyCharacteristics implements Parcelable {
return 0;
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
swEnforced.writeToParcel(out, flags);
hwEnforced.writeToParcel(out, flags);
@@ -57,5 +64,53 @@ public class KeyCharacteristics implements Parcelable {
swEnforced = KeymasterArguments.CREATOR.createFromParcel(in);
hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in);
}
+
+ public Integer getInteger(int tag) {
+ if (hwEnforced.containsTag(tag)) {
+ return hwEnforced.getInt(tag, -1);
+ } else if (swEnforced.containsTag(tag)) {
+ return swEnforced.getInt(tag, -1);
+ } else {
+ return null;
+ }
+ }
+
+ public int getInt(int tag, int defaultValue) {
+ Integer result = getInteger(tag);
+ return (result != null) ? result : defaultValue;
+ }
+
+ public List<Integer> getInts(int tag) {
+ List<Integer> result = new ArrayList<Integer>();
+ result.addAll(hwEnforced.getInts(tag));
+ result.addAll(swEnforced.getInts(tag));
+ return result;
+ }
+
+ public Date getDate(int tag) {
+ Date result = hwEnforced.getDate(tag, null);
+ if (result == null) {
+ result = swEnforced.getDate(tag, null);
+ }
+ return result;
+ }
+
+ public Date getDate(int tag, Date defaultValue) {
+ if (hwEnforced.containsTag(tag)) {
+ return hwEnforced.getDate(tag, null);
+ } else if (hwEnforced.containsTag(tag)) {
+ return swEnforced.getDate(tag, null);
+ } else {
+ return defaultValue;
+ }
+ }
+
+ public boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) {
+ if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+ return keyCharacteristics.hwEnforced.getBoolean(tag, false);
+ } else {
+ return keyCharacteristics.swEnforced.getBoolean(tag, false);
+ }
+ }
}
diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java
index b5fd4bd..8ed288c 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.java
+++ b/core/java/android/security/keymaster/KeymasterArguments.java
@@ -34,9 +34,12 @@ public class KeymasterArguments implements Parcelable {
public static final Parcelable.Creator<KeymasterArguments> CREATOR = new
Parcelable.Creator<KeymasterArguments>() {
+ @Override
public KeymasterArguments createFromParcel(Parcel in) {
return new KeymasterArguments(in);
}
+
+ @Override
public KeymasterArguments[] newArray(int size) {
return new KeymasterArguments[size];
}
@@ -54,6 +57,12 @@ public class KeymasterArguments implements Parcelable {
mArguments.add(new KeymasterIntArgument(tag, value));
}
+ public void addInts(int tag, int... values) {
+ for (int value : values) {
+ addInt(tag, value);
+ }
+ }
+
public void addBoolean(int tag) {
mArguments.add(new KeymasterBooleanArgument(tag));
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index e16691c..ea53c0d 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -47,12 +47,11 @@ public final class KeymasterDefs {
public static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1;
public static final int KM_TAG_ALGORITHM = KM_ENUM | 2;
public static final int KM_TAG_KEY_SIZE = KM_INT | 3;
- public static final int KM_TAG_BLOCK_MODE = KM_ENUM | 4;
- public static final int KM_TAG_DIGEST = KM_ENUM | 5;
- public static final int KM_TAG_MAC_LENGTH = KM_INT | 6;
- public static final int KM_TAG_PADDING = KM_ENUM | 7;
- public static final int KM_TAG_RETURN_UNAUTHED = KM_BOOL | 8;
- public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 9;
+ public static final int KM_TAG_BLOCK_MODE = KM_ENUM_REP | 4;
+ public static final int KM_TAG_DIGEST = KM_ENUM_REP | 5;
+ public static final int KM_TAG_PADDING = KM_ENUM_REP | 6;
+ public static final int KM_TAG_RETURN_UNAUTHED = KM_BOOL | 7;
+ public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 8;
public static final int KM_TAG_RESCOPING_ADD = KM_ENUM_REP | 101;
public static final int KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102;
@@ -85,6 +84,7 @@ public final class KeymasterDefs {
public static final int KM_TAG_NONCE = KM_BYTES | 1001;
public static final int KM_TAG_CHUNK_LENGTH = KM_INT | 1002;
public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1003;
+ public static final int KM_TAG_MAC_LENGTH = KM_INT | 1004;
// Algorithm values.
public static final int KM_ALGORITHM_RSA = 1;
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 822bfcc..29aaf30 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -41,6 +41,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.PhoneWindow;
+import android.view.SearchEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -332,6 +333,12 @@ public class DreamService extends Service implements Window.Callback {
/** {@inheritDoc} */
@Override
+ public boolean onSearchRequested(SearchEvent event) {
+ return onSearchRequested();
+ }
+
+ /** {@inheritDoc} */
+ @Override
public boolean onSearchRequested() {
return false;
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 13fb657..cf29310 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -215,7 +215,7 @@ public class TextToSpeech {
* </li>
* <li>
* A list of feature strings that engines might support, e.g
- * {@link Engine#KEY_FEATURE_NETWORK_SYNTHESIS}). These values may be passed in to
+ * {@link Engine#KEY_FEATURE_NETWORK_SYNTHESIS}. These values may be passed in to
* {@link TextToSpeech#speak} and {@link TextToSpeech#synthesizeToFile} to modify
* engine behaviour. The engine can be queried for the set of features it supports
* through {@link TextToSpeech#getFeatures(java.util.Locale)}.
@@ -576,9 +576,9 @@ public class TextToSpeech {
* @see TextToSpeech#getFeatures(java.util.Locale)
*
* @deprecated Starting from API level 21, to select network synthesis, call
- * ({@link TextToSpeech#getVoices()}, find a suitable network voice
+ * {@link TextToSpeech#getVoices()}, find a suitable network voice
* ({@link Voice#isNetworkConnectionRequired()}) and pass it
- * to {@link TextToSpeech#setVoice(Voice)}).
+ * to {@link TextToSpeech#setVoice(Voice)}.
*/
@Deprecated
public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 38f4d1c..5af2832 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -1916,7 +1916,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
break;
}
if (event.isTracking() && !event.isCanceled()) {
- launchDefaultSearch();
+ launchDefaultSearch(event);
}
return true;
}
@@ -4245,14 +4245,19 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
*
* @return true if search window opened
*/
- private boolean launchDefaultSearch() {
+ private boolean launchDefaultSearch(KeyEvent event) {
boolean result;
final Callback cb = getCallback();
if (cb == null || isDestroyed()) {
result = false;
} else {
sendCloseSystemWindows("search");
- result = cb.onSearchRequested();
+ int deviceId = event.getDeviceId();
+ SearchEvent searchEvent = null;
+ if (deviceId != 0) {
+ searchEvent = new SearchEvent(InputDevice.getDevice(deviceId));
+ }
+ result = cb.onSearchRequested(searchEvent);
}
if (!result && (getContext().getResources().getConfiguration().uiMode
& Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {
diff --git a/core/java/android/view/SearchEvent.java b/core/java/android/view/SearchEvent.java
new file mode 100644
index 0000000..ef51e7d
--- /dev/null
+++ b/core/java/android/view/SearchEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.InputDevice;
+
+/**
+ * Class that contains information about an event that triggers a search.
+ */
+public class SearchEvent {
+
+ private InputDevice mInputDevice;
+
+ /** @hide */
+ public SearchEvent(InputDevice inputDevice) {
+ mInputDevice = inputDevice;
+ }
+
+ /**
+ * Returns the {@link InputDevice} that triggered the search.
+ * @return InputDevice the InputDevice that triggered the search.
+ */
+ public InputDevice getInputDevice() {
+ return mInputDevice;
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b6f1e3b..60d2ceb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.annotation.Size;
import android.content.ClipData;
import android.content.Context;
+import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -3550,6 +3551,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static SparseArray<String> mAttributeMap;
/**
+ * @hide
+ */
+ String mStartActivityRequestWho;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -4915,6 +4921,58 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Call {@link Context#startActivityForResult(String, Intent, int, Bundle)} for the View's
+ * Context, creating a unique View identifier to retrieve the result.
+ *
+ * @param intent The Intent to be started.
+ * @param requestCode The request code to use.
+ * @hide
+ */
+ public void startActivityForResult(Intent intent, int requestCode) {
+ mStartActivityRequestWho = "@android:view:" + System.identityHashCode(this);
+ getContext().startActivityForResult(mStartActivityRequestWho, intent, requestCode, null);
+ }
+
+ /**
+ * If this View corresponds to the calling who, dispatches the activity result.
+ * @param who The identifier for the targeted View to receive the result.
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @return {@code true} if the activity result was dispatched.
+ * @hide
+ */
+ public boolean dispatchActivityResult(
+ String who, int requestCode, int resultCode, Intent data) {
+ if (mStartActivityRequestWho != null && mStartActivityRequestWho.equals(who)) {
+ onActivityResult(requestCode, resultCode, data);
+ mStartActivityRequestWho = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @hide
+ */
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // Do nothing.
+ }
+
+ /**
* Register a callback to be invoked when a hardware key is pressed in this view.
* Key presses in software input methods will generally not trigger the methods of
* this listener.
@@ -13980,6 +14038,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@CallSuper
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
+ if (mStartActivityRequestWho != null) {
+ BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
+ state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
+ return state;
+ }
return BaseSavedState.EMPTY_STATE;
}
@@ -14039,13 +14102,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@CallSuper
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
- if (state != BaseSavedState.EMPTY_STATE && state != null) {
+ if (state != null && !(state instanceof AbsSavedState)) {
throw new IllegalArgumentException("Wrong state class, expecting View State but "
+ "received " + state.getClass().toString() + " instead. This usually happens "
+ "when two views of different type have the same id in the same hierarchy. "
+ "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
+ "other views do not use the same id.");
}
+ if (state != null && state instanceof BaseSavedState) {
+ mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
+ }
}
/**
@@ -20735,6 +20801,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* state in {@link android.view.View#onSaveInstanceState()}.
*/
public static class BaseSavedState extends AbsSavedState {
+ String mStartActivityRequestWhoSaved;
+
/**
* Constructor used when reading from a parcel. Reads the state of the superclass.
*
@@ -20742,6 +20810,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public BaseSavedState(Parcel source) {
super(source);
+ mStartActivityRequestWhoSaved = source.readString();
}
/**
@@ -20753,6 +20822,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
super(superState);
}
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeString(mStartActivityRequestWhoSaved);
+ }
+
public static final Parcelable.Creator<BaseSavedState> CREATOR =
new Parcelable.Creator<BaseSavedState>() {
public BaseSavedState createFromParcel(Parcel in) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d0705bb..8d06ce2 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -19,6 +19,7 @@ package android.view;
import android.animation.LayoutTransition;
import android.annotation.IdRes;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.TypedArray;
@@ -814,6 +815,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * @hide
+ */
+ @Override
+ public boolean dispatchActivityResult(
+ String who, int requestCode, int resultCode, Intent data) {
+ if (super.dispatchActivityResult(who, requestCode, resultCode, data)) {
+ return true;
+ }
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.dispatchActivityResult(who, requestCode, resultCode, data)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Find the nearest view in the specified direction that wants to take
* focus.
*
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 36f047e..9d0d5ff 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -414,6 +414,15 @@ public abstract class Window {
public boolean onSearchRequested();
/**
+ * Called when the user signals the desire to start a search.
+ *
+ * @param searchEvent A {@link SearchEvent} describing the signal to
+ * start a search.
+ * @return true if search launched, false if activity refuses (blocks)
+ */
+ public boolean onSearchRequested(SearchEvent searchEvent);
+
+ /**
* Called when an action mode is being started for this window. Gives the
* callback an opportunity to handle the action mode in its own unique and
* beautiful way. If this method returns null the system can choose a way
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 979ee95..8ce1f8c 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -122,6 +122,11 @@ public class WindowCallbackWrapper implements Window.Callback {
}
@Override
+ public boolean onSearchRequested(SearchEvent searchEvent) {
+ return mWrapped.onSearchRequested(searchEvent);
+ }
+
+ @Override
public boolean onSearchRequested() {
return mWrapped.onSearchRequested();
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 4d1209a..0417921 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,6 +16,7 @@
package android.view.animation;
+import android.content.res.Configuration;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -44,6 +45,16 @@ public class AnimationUtils {
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
+ private static final float RECOMMENDED_FIELD_OF_VIEW_FOR_TV = 40f;
+ private static final float ESTIMATED_VIEWING_DISTANCE_FOR_WATCH = 11f;
+ private static final float AVERAGE_VIEWING_DISTANCE_FOR_PHONES = 14.2f;
+ private static final float N5_DIAGONAL_VIEW_ANGLE = 19.58f;
+ private static final float N5_DENSITY = 3.0f;
+ private static final float N5_DPI = 443f;
+
+ private static final float COTANGENT_OF_HALF_TV_ANGLE = (float) (1 / Math.tan(Math.toRadians
+ (RECOMMENDED_FIELD_OF_VIEW_FOR_TV / 2)));
+
/**
* Returns the current animation time in milliseconds. This time should be used when invoking
@@ -367,4 +378,78 @@ public class AnimationUtils {
}
return interpolator;
}
+
+ /**
+ * Derives the viewing distance of a device based on the device size (in inches), and the
+ * device type.
+ * @hide
+ */
+ public static float getViewingDistance(float width, float height, int uiMode) {
+ if (uiMode == Configuration.UI_MODE_TYPE_TELEVISION) {
+ // TV
+ return (width / 2) * COTANGENT_OF_HALF_TV_ANGLE;
+ } else if (uiMode == Configuration.UI_MODE_TYPE_WATCH) {
+ // Watch
+ return ESTIMATED_VIEWING_DISTANCE_FOR_WATCH;
+ } else {
+ // Tablet, phone, etc
+ return AVERAGE_VIEWING_DISTANCE_FOR_PHONES;
+ }
+ }
+
+ /**
+ * Calculates the duration scaling factor of an animation based on the hint that the animation
+ * will move across the entire screen. A scaling factor of 1 means the duration on this given
+ * device will be the same as the duration set through
+ * {@link android.animation.Animator#setDuration(long)}. The calculation uses Nexus 5 as a
+ * baseline device. That is, the duration of the animation on a given device will scale its
+ * duration so that it has the same look and feel as the animation on Nexus 5. In order to
+ * achieve the same perceived effect of the animation across different devices, we maintain
+ * the same angular speed of the same animation in users' field of view. Therefore, the
+ * duration scale factor is determined by the ratio of the angular movement on current
+ * devices to that on the baseline device.
+ *
+ * @param width width of the screen (in inches)
+ * @param height height of the screen (in inches)
+ * @param viewingDistance the viewing distance of the device (i.e. watch, phone, TV, etc) in
+ * inches
+ * @return scaling factor (or multiplier) of the duration set through
+ * {@link android.animation.Animator#setDuration(long)} on current device.
+ * @hide
+ */
+ public static float getScreenSizeBasedDurationScale(float width, float height,
+ float viewingDistance) {
+ // Animation's moving distance is proportional to the screen size.
+ float diagonal = (float) Math.sqrt(width * width + height * height);
+ float diagonalViewAngle = (float) Math.toDegrees(Math.atan((diagonal / 2f)
+ / viewingDistance) * 2);
+ return diagonalViewAngle / N5_DIAGONAL_VIEW_ANGLE;
+ }
+
+ /**
+ * Calculates the duration scaling factor of an animation under the assumption that the
+ * animation is defined to move the same amount of distance (in dp) across all devices. A
+ * scaling factor of 1 means the duration on this given device will be the same as the
+ * duration set through {@link android.animation.Animator#setDuration(long)}. The calculation
+ * uses Nexus 5 as a baseline device. That is, the duration of the animation on a given
+ * device will scale its duration so that it has the same look and feel as the animation on
+ * Nexus 5. In order to achieve the same perceived effect of the animation across different
+ * devices, we maintain the same angular velocity of the same animation in users' field of
+ * view. Therefore, the duration scale factor is determined by the ratio of the angular
+ * movement on current devices to that on the baseline device.
+ *
+ * @param density logical density of the display. {@link android.util.DisplayMetrics#density}
+ * @param dpi pixels per inch
+ * @param viewingDistance viewing distance of the device (in inches)
+ * @return the scaling factor of duration
+ * @hide
+ */
+ public static float getDpBasedDurationScale(float density, float dpi,
+ float viewingDistance) {
+ // Angle in users' field of view per dp:
+ float anglePerDp = (float) Math.atan2((density / dpi) / 2, viewingDistance) * 2;
+ float baselineAnglePerDp = (float) Math.atan2((N5_DENSITY / N5_DPI) / 2,
+ AVERAGE_VIEWING_DISTANCE_FOR_PHONES) * 2;
+ return anglePerDp / baselineAnglePerDp;
+ }
}
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index a157087..06a5bd2 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -534,22 +534,23 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
@Override
public void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
+ final SavedState ss = (SavedState) state;
// TODO: Move instance state into DayPickerView, YearPickerView.
mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
- mCurrentView = ss.getCurrentView();
mMinDate.setTimeInMillis(ss.getMinDate());
mMaxDate.setTimeInMillis(ss.getMaxDate());
onCurrentDateChanged(false);
- setCurrentView(mCurrentView);
+
+ final int currentView = ss.getCurrentView();
+ setCurrentView(currentView);
final int listPosition = ss.getListPosition();
if (listPosition != -1) {
- if (mCurrentView == VIEW_MONTH_DAY) {
+ if (currentView == VIEW_MONTH_DAY) {
mDayPickerView.setCurrentItem(listPosition);
- } else if (mCurrentView == VIEW_YEAR) {
+ } else if (currentView == VIEW_YEAR) {
final int listPositionOffset = ss.getListPositionOffset();
mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
}
@@ -601,7 +602,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
* Class for managing state storing/restoring.
*/
private static class SavedState extends View.BaseSavedState {
-
private final int mSelectedYear;
private final int mSelectedMonth;
private final int mSelectedDay;
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index ec2528f..0e0b2d3 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -22,9 +22,12 @@ import com.android.internal.R;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.MathUtils;
+import android.view.View;
+import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
@@ -41,6 +44,8 @@ class DayPickerView extends ViewPager {
private final Calendar mMinDate = Calendar.getInstance();
private final Calendar mMaxDate = Calendar.getInstance();
+ private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
private final DayPickerAdapter mAdapter;
/** Temporary calendar used for date calculations. */
@@ -140,6 +145,93 @@ class DayPickerView extends ViewPager {
});
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ populate();
+
+ // Everything below is mostly copied from FrameLayout.
+ int count = getChildCount();
+
+ final boolean measureMatchParentChildren =
+ MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+ maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
+ if (measureMatchParentChildren) {
+ if (lp.width == LayoutParams.MATCH_PARENT ||
+ lp.height == LayoutParams.MATCH_PARENT) {
+ mMatchParentChildren.add(child);
+ }
+ }
+ }
+ }
+
+ // Account for padding too
+ maxWidth += getPaddingLeft() + getPaddingRight();
+ maxHeight += getPaddingTop() + getPaddingBottom();
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ // Check against our foreground's minimum height and width
+ final Drawable drawable = getForeground();
+ if (drawable != null) {
+ maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
+ maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
+ }
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+
+ count = mMatchParentChildren.size();
+ if (count > 1) {
+ for (int i = 0; i < count; i++) {
+ final View child = mMatchParentChildren.get(i);
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int childWidthMeasureSpec;
+ final int childHeightMeasureSpec;
+
+ if (lp.width == LayoutParams.MATCH_PARENT) {
+ childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
+ MeasureSpec.EXACTLY);
+ } else {
+ childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+ getPaddingLeft() + getPaddingRight(),
+ lp.width);
+ }
+
+ if (lp.height == LayoutParams.MATCH_PARENT) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
+ MeasureSpec.EXACTLY);
+ } else {
+ childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+ getPaddingTop() + getPaddingBottom(),
+ lp.height);
+ }
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+ }
+
+ mMatchParentChildren.clear();
+ }
+
public void setDayOfWeekTextAppearance(int resId) {
mAdapter.setDayOfWeekTextAppearance(resId);
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 491826a..29073be 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -27,6 +27,7 @@ import android.content.UndoManager;
import android.content.UndoOperation;
import android.content.UndoOwner;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -109,6 +110,7 @@ import java.text.BreakIterator;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
/**
* Helper class used by TextView to handle editable text views.
@@ -2980,6 +2982,8 @@ public class Editor {
}
}
+ addIntentMenuItemsForTextProcessing(menu);
+
if (menu.hasVisibleItems() || mode.getCustomView() != null) {
mTextView.setHasTransientState(true);
return true;
@@ -3036,6 +3040,32 @@ public class Editor {
styledAttributes.recycle();
}
+ private void addIntentMenuItemsForTextProcessing(Menu menu) {
+ if (mTextView.canProcessText()) {
+ PackageManager packageManager = mTextView.getContext().getPackageManager();
+ List<ResolveInfo> supportedActivities =
+ packageManager.queryIntentActivities(createProcessTextIntent(), 0);
+ for (ResolveInfo info : supportedActivities) {
+ menu.add(info.loadLabel(packageManager))
+ .setIntent(createProcessTextIntentForResolveInfo(info))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
+ | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+ }
+ }
+
+ private Intent createProcessTextIntent() {
+ return new Intent()
+ .setAction(Intent.ACTION_PROCESS_TEXT)
+ .setType("text/plain");
+ }
+
+ private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) {
+ return createProcessTextIntent()
+ .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !mTextView.isTextEditable())
+ .setClassName(info.activityInfo.packageName, info.activityInfo.name);
+ }
+
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
updateReplaceItem(menu);
@@ -3060,6 +3090,13 @@ public class Editor {
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (item.getIntent() != null
+ && item.getIntent().getAction().equals(Intent.ACTION_PROCESS_TEXT)) {
+ item.getIntent().putExtra(Intent.EXTRA_PROCESS_TEXT, mTextView.getSelectedText());
+ mTextView.startActivityForResult(
+ item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE);
+ return true;
+ }
if (mCustomSelectionActionModeCallback != null &&
mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) {
return true;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 205d35e..24e9cbe 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -60,7 +60,6 @@ import android.widget.RemoteViews.RemoteView;
import java.util.ArrayList;
-
/**
* <p>
* Visual indicator of progress in some operation. Displays a bar to the user
@@ -266,9 +265,14 @@ public class ProgressBar extends View {
final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
if (progressDrawable != null) {
- // Calling this method can set mMaxHeight, make sure the corresponding
- // XML attribute for mMaxHeight is read after calling this method
- setProgressDrawableTiled(progressDrawable);
+ // Calling setProgressDrawable can set mMaxHeight, so make sure the
+ // corresponding XML attribute for mMaxHeight is read after calling
+ // this method.
+ if (needsTileify(progressDrawable)) {
+ setProgressDrawableTiled(progressDrawable);
+ } else {
+ setProgressDrawable(progressDrawable);
+ }
}
@@ -292,13 +296,17 @@ public class ProgressBar extends View {
setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));
- setSecondaryProgress(
- a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
+ setSecondaryProgress(a.getInt(
+ R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
final Drawable indeterminateDrawable = a.getDrawable(
R.styleable.ProgressBar_indeterminateDrawable);
if (indeterminateDrawable != null) {
- setIndeterminateDrawableTiled(indeterminateDrawable);
+ if (needsTileify(indeterminateDrawable)) {
+ setIndeterminateDrawableTiled(indeterminateDrawable);
+ } else {
+ setIndeterminateDrawable(indeterminateDrawable);
+ }
}
mOnlyIndeterminate = a.getBoolean(
@@ -395,6 +403,45 @@ public class ProgressBar extends View {
}
/**
+ * Returns {@code true} if the target drawable needs to be tileified.
+ *
+ * @param dr the drawable to check
+ * @return {@code true} if the target drawable needs to be tileified,
+ * {@code false} otherwise
+ */
+ private static boolean needsTileify(Drawable dr) {
+ if (dr instanceof LayerDrawable) {
+ final LayerDrawable orig = (LayerDrawable) dr;
+ final int N = orig.getNumberOfLayers();
+ for (int i = 0; i < N; i++) {
+ if (needsTileify(orig.getDrawable(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (dr instanceof StateListDrawable) {
+ final StateListDrawable in = (StateListDrawable) dr;
+ final int N = in.getStateCount();
+ for (int i = 0; i < N; i++) {
+ if (needsTileify(in.getStateDrawable(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // If there's a bitmap that's not wrapped with a ClipDrawable or
+ // ScaleDrawable, we'll need to wrap it and apply tiling.
+ if (dr instanceof BitmapDrawable) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* Converts a drawable to a tiled version of itself. It will recursively
* traverse layer and state list drawables.
*/
@@ -448,18 +495,14 @@ public class ProgressBar extends View {
mSampleTile = tileBitmap;
}
- final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
- final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
- Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
- shapeDrawable.getPaint().setShader(bitmapShader);
+ final BitmapDrawable clone = (BitmapDrawable) bitmap.getConstantState().newDrawable();
+ clone.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
- // Ensure the tint and filter are propagated in the correct order.
- shapeDrawable.setTintList(bitmap.getTint());
- shapeDrawable.setTintMode(bitmap.getTintMode());
- shapeDrawable.setColorFilter(bitmap.getColorFilter());
-
- return clip ? new ClipDrawable(
- shapeDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL) : shapeDrawable;
+ if (clip) {
+ return new ClipDrawable(clone, Gravity.LEFT, ClipDrawable.HORIZONTAL);
+ } else {
+ return clone;
+ }
}
return drawable;
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index d9f1f0e..aa7f0b6 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -585,7 +585,6 @@ class SimpleMonthView extends View {
mToday = day;
}
}
- mNumWeeks = calculateNumRows();
// Invalidate the old title.
mTitle = null;
@@ -616,18 +615,6 @@ class SimpleMonthView extends View {
}
}
- public void reuse() {
- mNumWeeks = MAX_WEEKS_IN_MONTH;
- requestLayout();
- }
-
- private int calculateNumRows() {
- final int offset = findDayOffset();
- final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK;
- final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK;
- return dividend + (remainder > 0 ? 1 : 0);
- }
-
private boolean sameDay(int day, Calendar today) {
return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
&& day == today.get(Calendar.DAY_OF_MONTH);
@@ -635,8 +622,9 @@ class SimpleMonthView extends View {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int preferredHeight = mDesiredDayHeight * mNumWeeks + mDesiredDayOfWeekHeight
- + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom();
+ final int preferredHeight = mDesiredDayHeight * MAX_WEEKS_IN_MONTH
+ + mDesiredDayOfWeekHeight + mDesiredMonthHeight
+ + getPaddingTop() + getPaddingBottom();
final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK
+ getPaddingStart() + getPaddingEnd();
final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 11439e4..9bbf375 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -27,6 +27,7 @@ import android.annotation.XmlRes;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.Intent;
import android.content.UndoManager;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
@@ -289,6 +290,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// System wide time for last cut or copy action.
static long LAST_CUT_OR_COPY_TIME;
+ /**
+ * @hide
+ */
+ static final int PROCESS_TEXT_REQUEST_CODE = 100;
+
private ColorStateList mTextColor;
private ColorStateList mHintTextColor;
private ColorStateList mLinkTextColor;
@@ -534,6 +540,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private Layout mLayout;
private boolean mLocaleChanged = false;
+ @ViewDebug.ExportedProperty(category = "text")
private int mGravity = Gravity.TOP | Gravity.START;
private boolean mHorizontallyScrolling;
@@ -1414,6 +1421,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
+ CharSequence result = data != null
+ ? data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)
+ : "";
+ if (isTextEditable()) {
+ replaceSelectionWithText(result);
+ } else {
+ Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
private void setTypefaceFromAttrs(String familyName, int typefaceIndex, int styleIndex) {
Typeface tf = null;
if (familyName != null) {
@@ -7474,6 +7498,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return selectionStart >= 0 && selectionStart != selectionEnd;
}
+ String getSelectedText() {
+ if (hasSelection()) {
+ return String.valueOf(mText.subSequence(getSelectionStart(), getSelectionEnd()));
+ }
+ return null;
+ }
+
/**
* Sets the properties of this field (lines, horizontally scrolling,
* transformation method) to be for a single-line input.
@@ -9066,6 +9097,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hasPrimaryClip());
}
+ boolean canProcessText() {
+ if (!getContext().canStartActivityForResult() || getId() == View.NO_ID
+ || hasPasswordTransformationMethod()) {
+ return false;
+ }
+
+ if (mText.length() > 0 && hasSelection() && mEditor != null) {
+ return true;
+ }
+
+ return false;
+ }
+
boolean selectAllText() {
// Need to hide insert point cursor controller before settings selection, otherwise insert
// point cursor controller obtains cursor update event and update cursor with cancelling
@@ -9078,6 +9122,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return length > 0;
}
+ void replaceSelectionWithText(CharSequence text) {
+ ((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text);
+ }
+
/**
* Paste clipboard content between min and max positions.
*/
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 7182414..89e59f9 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -178,24 +178,29 @@ class YearPickerView extends ListView {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ final TextView v;
+ final boolean hasNewView = convertView == null;
+ if (hasNewView) {
+ v = (TextView) mInflater.inflate(ITEM_LAYOUT, parent, false);
+ } else {
+ v = (TextView) convertView;
}
final int year = getYearForPosition(position);
final boolean activated = mActivatedYear == year;
- final int textAppearanceResId;
- if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) {
- textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE;
- } else {
- textAppearanceResId = ITEM_TEXT_APPEARANCE;
+ if (hasNewView || v.isActivated() != activated) {
+ final int textAppearanceResId;
+ if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) {
+ textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE;
+ } else {
+ textAppearanceResId = ITEM_TEXT_APPEARANCE;
+ }
+ v.setTextAppearance(textAppearanceResId);
+ v.setActivated(activated);
}
- final TextView v = (TextView) convertView;
- v.setText("" + year);
- v.setTextAppearance(v.getContext(), textAppearanceResId);
- v.setActivated(activated);
+ v.setText(Integer.toString(year));
return v;
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 9dabb4e..b8110e3 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -170,9 +170,8 @@ public class AlertController {
}
private static boolean shouldCenterSingleButton(Context context) {
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogCenterButtons,
- outValue, true);
+ final TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
return outValue.data != 0;
}
@@ -182,27 +181,25 @@ public class AlertController {
mWindow = window;
mHandler = new ButtonHandler(di);
- TypedArray a = context.obtainStyledAttributes(null,
- com.android.internal.R.styleable.AlertDialog,
- com.android.internal.R.attr.alertDialogStyle, 0);
+ final TypedArray a = context.obtainStyledAttributes(null,
+ R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
- mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout,
- com.android.internal.R.layout.alert_dialog);
+ mAlertDialogLayout = a.getResourceId(
+ R.styleable.AlertDialog_layout, R.layout.alert_dialog);
mButtonPanelSideLayout = a.getResourceId(
- com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0);
-
+ R.styleable.AlertDialog_buttonPanelSideLayout, 0);
mListLayout = a.getResourceId(
- com.android.internal.R.styleable.AlertDialog_listLayout,
- com.android.internal.R.layout.select_dialog);
+ R.styleable.AlertDialog_listLayout, R.layout.select_dialog);
+
mMultiChoiceItemLayout = a.getResourceId(
- com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout,
- com.android.internal.R.layout.select_dialog_multichoice);
+ R.styleable.AlertDialog_multiChoiceItemLayout,
+ R.layout.select_dialog_multichoice);
mSingleChoiceItemLayout = a.getResourceId(
- com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
- com.android.internal.R.layout.select_dialog_singlechoice);
+ R.styleable.AlertDialog_singleChoiceItemLayout,
+ R.layout.select_dialog_singlechoice);
mListItemLayout = a.getResourceId(
- com.android.internal.R.styleable.AlertDialog_listItemLayout,
- com.android.internal.R.layout.select_dialog_item);
+ R.styleable.AlertDialog_listItemLayout,
+ R.layout.select_dialog_item);
a.recycle();
}
@@ -1067,9 +1064,9 @@ public class AlertController {
}
private void createListView(final AlertController dialog) {
- final RecycleListView listView = (RecycleListView)
- mInflater.inflate(dialog.mListLayout, null);
- ListAdapter adapter;
+ final RecycleListView listView =
+ (RecycleListView) mInflater.inflate(dialog.mListLayout, null);
+ final ListAdapter adapter;
if (mIsMultiChoice) {
if (mCursor == null) {
@@ -1115,14 +1112,20 @@ public class AlertController {
};
}
} else {
- int layout = mIsSingleChoice
- ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
- if (mCursor == null) {
- adapter = (mAdapter != null) ? mAdapter
- : new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
+ final int layout;
+ if (mIsSingleChoice) {
+ layout = dialog.mSingleChoiceItemLayout;
+ } else {
+ layout = dialog.mListItemLayout;
+ }
+
+ if (mCursor != null) {
+ adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
+ new String[] { mLabelColumn }, new int[] { R.id.text1 });
+ } else if (mAdapter != null) {
+ adapter = mAdapter;
} else {
- adapter = new SimpleCursorAdapter(mContext, layout,
- mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
+ adapter = new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
}
}
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
index ee225a1..6aa81ce 100644
--- a/core/java/com/android/internal/logging/MetricsConstants.java
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -69,7 +69,9 @@ public interface MetricsConstants {
public static final int DEVELOPMENT = 39;
public static final int DEVICEINFO = 40;
public static final int DEVICEINFO_IMEI_INFORMATION = 41;
+ @Deprecated
public static final int DEVICEINFO_MEMORY = 42;
+ public static final int DEVICEINFO_STORAGE = 42;
public static final int DEVICEINFO_SIM_STATUS = 43;
public static final int DEVICEINFO_STATUS = 44;
public static final int DEVICEINFO_USB = 45;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 024b7c5..59dbec6 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -21,7 +21,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.SensorManager;
import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Bundle;
@@ -130,16 +129,11 @@ public final class BatteryStatsHelper {
return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
}
- public static boolean checkHasWifiPowerReporting(Context context, PowerProfile profile) {
- WifiManager manager = context.getSystemService(WifiManager.class);
- if (manager.isEnhancedPowerReportingSupported()) {
- if (profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
- profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
- profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0) {
- return true;
- }
- }
- return false;
+ public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
+ return stats.hasWifiActivityReporting() &&
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
}
public BatteryStatsHelper(Context context) {
@@ -339,7 +333,7 @@ public final class BatteryStatsHelper {
mMobileRadioPowerCalculator.reset(mStats);
if (mWifiPowerCalculator == null) {
- if (checkHasWifiPowerReporting(mContext, mPowerProfile)) {
+ if (checkHasWifiPowerReporting(mStats, mPowerProfile)) {
mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
} else {
mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c5c0ba6..fbb2dfc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -472,6 +472,8 @@ public final class BatteryStatsImpl extends BatteryStats {
private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
private PowerProfile mPowerProfile;
+ private boolean mHasWifiEnergyReporting = false;
+ private boolean mHasBluetoothEnergyReporting = false;
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
@@ -4298,6 +4300,10 @@ public final class BatteryStatsImpl extends BatteryStats {
return mBluetoothStateTimer[bluetoothState].getCountLocked(which);
}
+ @Override public boolean hasBluetoothActivityReporting() {
+ return mHasBluetoothEnergyReporting;
+ }
+
@Override public long getBluetoothControllerActivity(int type, int which) {
if (type >= 0 && type < mBluetoothActivityCounters.length) {
return mBluetoothActivityCounters[type].getCountLocked(which);
@@ -4305,6 +4311,10 @@ public final class BatteryStatsImpl extends BatteryStats {
return 0;
}
+ @Override public boolean hasWifiActivityReporting() {
+ return mHasWifiEnergyReporting;
+ }
+
@Override public long getWifiControllerActivity(int type, int which) {
if (type >= 0 && type < mWifiActivityCounters.length) {
return mWifiActivityCounters[type].getCountLocked(which);
@@ -7567,6 +7577,8 @@ public final class BatteryStatsImpl extends BatteryStats {
}
if (info != null) {
+ mHasWifiEnergyReporting = true;
+
// Measured in mAms
final long txTimeMs = info.getControllerTxTimeMillis();
final long rxTimeMs = info.getControllerRxTimeMillis();
@@ -7778,6 +7790,7 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
if (info != null && mOnBatteryInternal && false) {
+ mHasBluetoothEnergyReporting = true;
mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
info.getControllerRxTimeMillis());
mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
@@ -9533,6 +9546,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mWifiActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
+ mHasWifiEnergyReporting = in.readInt() != 0;
+ mHasBluetoothEnergyReporting = in.readInt() != 0;
mNumConnectivityChange = in.readInt();
mLoadedNumConnectivityChange = in.readInt();
mUnpluggedNumConnectivityChange = in.readInt();
@@ -9686,6 +9701,8 @@ public final class BatteryStatsImpl extends BatteryStats {
for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
mWifiActivityCounters[i].writeToParcel(out);
}
+ out.writeInt(mHasWifiEnergyReporting ? 1 : 0);
+ out.writeInt(mHasBluetoothEnergyReporting ? 1 : 0);
out.writeInt(mNumConnectivityChange);
out.writeInt(mLoadedNumConnectivityChange);
out.writeInt(mUnpluggedNumConnectivityChange);
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 4e77f6b..4fb8b55 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -16,12 +16,15 @@
package com.android.internal.os;
import android.os.BatteryStats;
+import android.util.Log;
/**
* WiFi power calculator for when BatteryStats supports energy reporting
* from the WiFi controller.
*/
public class WifiPowerCalculator extends PowerCalculator {
+ private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+ private static final String TAG = "WifiPowerCalculator";
private final double mIdleCurrentMa;
private final double mTxCurrentMa;
private final double mRxCurrentMa;
@@ -75,6 +78,10 @@ public class WifiPowerCalculator extends PowerCalculator {
+ (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
}
app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
+
+ if (DEBUG) {
+ Log.d(TAG, "left over WiFi power: " + BatteryStatsHelper.makemAh(app.wifiPowerMah));
+ }
}
@Override
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f908fcb..62e724a 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.util;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.ArraySet;
import dalvik.system.VMRuntime;
@@ -24,13 +26,13 @@ import libcore.util.EmptyArray;
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.Objects;
/**
* ArrayUtils contains some methods that you can call to find out
* the most efficient increments by which to grow arrays.
*/
-public class ArrayUtils
-{
+public class ArrayUtils {
private static final int CACHE_SIZE = 73;
private static Object[] sCache = new Object[CACHE_SIZE];
@@ -158,11 +160,7 @@ public class ArrayUtils
public static <T> int indexOf(T[] array, T value) {
if (array == null) return -1;
for (int i = 0; i < array.length; i++) {
- if (array[i] == null) {
- if (value == null) return i;
- } else {
- if (value != null && array[i].equals(value)) return i;
- }
+ if (Objects.equals(array[i], value)) return i;
}
return -1;
}
@@ -209,17 +207,15 @@ public class ArrayUtils
}
/**
- * Appends an element to a copy of the array and returns the copy.
- * @param array The original array, or null to represent an empty array.
- * @param element The element to add.
- * @return A new array that contains all of the elements of the original array
- * with the specified element added at the end.
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
*/
@SuppressWarnings("unchecked")
- public static <T> T[] appendElement(Class<T> kind, T[] array, T element) {
+ public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
final T[] result;
final int end;
if (array != null) {
+ if (contains(array, element)) return array;
end = array.length;
result = (T[])Array.newInstance(kind, end + 1);
System.arraycopy(array, 0, result, 0, end);
@@ -232,21 +228,15 @@ public class ArrayUtils
}
/**
- * Removes an element from a copy of the array and returns the copy.
- * If the element is not present, then the original array is returned unmodified.
- * @param array The original array, or null to represent an empty array.
- * @param element The element to remove.
- * @return A new array that contains all of the elements of the original array
- * except the first copy of the specified element removed. If the specified element
- * was not present, then returns the original array. Returns null if the result
- * would be an empty array.
+ * Removes value from given array if present, providing set-like behavior.
*/
@SuppressWarnings("unchecked")
- public static <T> T[] removeElement(Class<T> kind, T[] array, T element) {
+ public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
if (array != null) {
+ if (!contains(array, element)) return array;
final int length = array.length;
for (int i = 0; i < length; i++) {
- if (array[i] == element) {
+ if (Objects.equals(array[i], element)) {
if (length == 1) {
return null;
}
@@ -261,14 +251,10 @@ public class ArrayUtils
}
/**
- * Appends a new value to a copy of the array and returns the copy. If
- * the value is already present, the original array is returned
- * @param cur The original array, or null to represent an empty array.
- * @param val The value to add.
- * @return A new array that contains all of the values of the original array
- * with the new value added, or the original array.
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
*/
- public static int[] appendInt(int[] cur, int val) {
+ public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
if (cur == null) {
return new int[] { val };
}
@@ -284,7 +270,10 @@ public class ArrayUtils
return ret;
}
- public static int[] removeInt(int[] cur, int val) {
+ /**
+ * Removes value from given array if present, providing set-like behavior.
+ */
+ public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
if (cur == null) {
return null;
}
@@ -305,14 +294,10 @@ public class ArrayUtils
}
/**
- * Appends a new value to a copy of the array and returns the copy. If
- * the value is already present, the original array is returned
- * @param cur The original array, or null to represent an empty array.
- * @param val The value to add.
- * @return A new array that contains all of the values of the original array
- * with the new value added, or the original array.
+ * Adds value to given array if not already present, providing set-like
+ * behavior.
*/
- public static long[] appendLong(long[] cur, long val) {
+ public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
if (cur == null) {
return new long[] { val };
}
@@ -328,7 +313,10 @@ public class ArrayUtils
return ret;
}
- public static long[] removeLong(long[] cur, long val) {
+ /**
+ * Removes value from given array if present, providing set-like behavior.
+ */
+ public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
if (cur == null) {
return null;
}
diff --git a/core/java/com/android/internal/widget/DialogViewAnimator.java b/core/java/com/android/internal/widget/DialogViewAnimator.java
new file mode 100644
index 0000000..bdfc1af
--- /dev/null
+++ b/core/java/com/android/internal/widget/DialogViewAnimator.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ViewAnimator;
+
+import java.util.ArrayList;
+
+/**
+ * ViewAnimator with a more reasonable handling of MATCH_PARENT.
+ */
+public class DialogViewAnimator extends ViewAnimator {
+ private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+ public DialogViewAnimator(Context context) {
+ super(context);
+ }
+
+ public DialogViewAnimator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final boolean measureMatchParentChildren =
+ MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ // First measure all children and record maximum dimensions where the
+ // spec isn't MATCH_PARENT.
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (getMeasureAllChildren() || child.getVisibility() != GONE) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final boolean matchWidth = lp.width == LayoutParams.MATCH_PARENT;
+ final boolean matchHeight = lp.height == LayoutParams.MATCH_PARENT;
+ if (measureMatchParentChildren && (matchWidth || matchHeight)) {
+ mMatchParentChildren.add(child);
+ }
+
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+
+ // Measured dimensions only count against the maximum
+ // dimensions if they're not MATCH_PARENT.
+ int state = 0;
+
+ if (measureMatchParentChildren && !matchWidth) {
+ maxWidth = Math.max(maxWidth, child.getMeasuredWidth()
+ + lp.leftMargin + lp.rightMargin);
+ state |= child.getMeasuredWidthAndState() & MEASURED_STATE_MASK;
+ }
+
+ if (measureMatchParentChildren && !matchHeight) {
+ maxHeight = Math.max(maxHeight, child.getMeasuredHeight()
+ + lp.topMargin + lp.bottomMargin);
+ state |= (child.getMeasuredHeightAndState() >> MEASURED_HEIGHT_STATE_SHIFT)
+ & (MEASURED_STATE_MASK >> MEASURED_HEIGHT_STATE_SHIFT);
+ }
+
+ childState = combineMeasuredStates(childState, state);
+ }
+ }
+
+ // Account for padding too.
+ maxWidth += getPaddingLeft() + getPaddingRight();
+ maxHeight += getPaddingTop() + getPaddingBottom();
+
+ // Check against our minimum height and width.
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ // Check against our foreground's minimum height and width.
+ final Drawable drawable = getForeground();
+ if (drawable != null) {
+ maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
+ maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
+ }
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+
+ // Measure remaining MATCH_PARENT children again using real dimensions.
+ final int matchCount = mMatchParentChildren.size();
+ for (int i = 0; i < matchCount; i++) {
+ final View child = mMatchParentChildren.get(i);
+ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+ final int childWidthMeasureSpec;
+ if (lp.width == LayoutParams.MATCH_PARENT) {
+ childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getMeasuredWidth() - getPaddingLeft() - getPaddingRight()
+ - lp.leftMargin - lp.rightMargin,
+ MeasureSpec.EXACTLY);
+ } else {
+ childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+ getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin,
+ lp.width);
+ }
+
+ final int childHeightMeasureSpec;
+ if (lp.height == LayoutParams.MATCH_PARENT) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
+ - lp.topMargin - lp.bottomMargin,
+ MeasureSpec.EXACTLY);
+ } else {
+ childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+ getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin,
+ lp.height);
+ }
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ mMatchParentChildren.clear();
+ }
+}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 8d66191..5c08daf 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -889,7 +889,7 @@ public class ViewPager extends ViewGroup {
}
}
- void populate() {
+ public void populate() {
populate(mCurItem);
}