Merge "Sealed sessions need to be destroyed or validated" into oc-mr1-dev
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index cec5db9..a558d68 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Region;
-import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -52,8 +51,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
/**
* Accessibility services should only be used to assist users with disabilities in using
* Android devices and apps. They run in the background and receive callbacks by the system
@@ -394,7 +391,7 @@
public static final int SHOW_MODE_AUTO = 0;
public static final int SHOW_MODE_HIDDEN = 1;
- private int mConnectionId;
+ private int mConnectionId = AccessibilityInteractionClient.NO_ID;
private AccessibilityServiceInfo mInfo;
@@ -1612,7 +1609,7 @@
private final Callbacks mCallback;
- private int mConnectionId;
+ private int mConnectionId = AccessibilityInteractionClient.NO_ID;
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
@@ -1707,7 +1704,8 @@
if (event != null) {
// Send the event to AccessibilityCache via AccessibilityInteractionClient
AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
- if (serviceWantsEvent) {
+ if (serviceWantsEvent
+ && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
// Send the event to AccessibilityService
mCallback.onAccessibilityEvent(event);
}
@@ -1721,7 +1719,9 @@
} return;
case DO_ON_INTERRUPT: {
- mCallback.onInterrupt();
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onInterrupt();
+ }
} return;
case DO_INIT: {
@@ -1746,8 +1746,10 @@
} return;
case DO_ON_GESTURE: {
- final int gestureId = message.arg1;
- mCallback.onGesture(gestureId);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final int gestureId = message.arg1;
+ mCallback.onGesture(gestureId);
+ }
} return;
case DO_CLEAR_ACCESSIBILITY_CACHE: {
@@ -1779,37 +1781,51 @@
} return;
case DO_ON_MAGNIFICATION_CHANGED: {
- final SomeArgs args = (SomeArgs) message.obj;
- final Region region = (Region) args.arg1;
- final float scale = (float) args.arg2;
- final float centerX = (float) args.arg3;
- final float centerY = (float) args.arg4;
- mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final SomeArgs args = (SomeArgs) message.obj;
+ final Region region = (Region) args.arg1;
+ final float scale = (float) args.arg2;
+ final float centerX = (float) args.arg3;
+ final float centerY = (float) args.arg4;
+ mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+ }
} return;
case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
- final int showMode = (int) message.arg1;
- mCallback.onSoftKeyboardShowModeChanged(showMode);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final int showMode = (int) message.arg1;
+ mCallback.onSoftKeyboardShowModeChanged(showMode);
+ }
} return;
case DO_GESTURE_COMPLETE: {
- final boolean successfully = message.arg2 == 1;
- mCallback.onPerformGestureResult(message.arg1, successfully);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final boolean successfully = message.arg2 == 1;
+ mCallback.onPerformGestureResult(message.arg1, successfully);
+ }
} return;
case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
- mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
+ }
} return;
case DO_ON_FINGERPRINT_GESTURE: {
- mCallback.onFingerprintGesture(message.arg1);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onFingerprintGesture(message.arg1);
+ }
} return;
case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
- mCallback.onAccessibilityButtonClicked();
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onAccessibilityButtonClicked();
+ }
} return;
case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {
- final boolean available = (message.arg1 != 0);
- mCallback.onAccessibilityButtonAvailabilityChanged(available);
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final boolean available = (message.arg1 != 0);
+ mCallback.onAccessibilityButtonAvailabilityChanged(available);
+ }
} return;
default :
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ad7a5ab..664bcbca 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -718,6 +718,10 @@
* Cycles do not exist because they are illegal and screened for during installation.
*
* May be null if no splits are installed, or if no dependencies exist between them.
+ *
+ * NOTE: Any change to the way split dependencies are stored must update the logic that
+ * creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts).
+ *
* @hide
*/
public SparseArray<int[]> splitDependencies;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 0d5c5e3..bfeb14d 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1769,7 +1769,7 @@
}
@Override
- public void onRepeatingRequestError(long lastFrameNumber) {
+ public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
if (DEBUG) {
Log.d(TAG, "Repeating request error received. Last frame number is " +
lastFrameNumber);
@@ -1782,7 +1782,10 @@
}
checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
- mRepeatingRequestId = REQUEST_ID_NONE;
+ // Check if there is already a new repeating request
+ if (mRepeatingRequestId == repeatingRequestId) {
+ mRepeatingRequestId = REQUEST_ID_NONE;
+ }
}
}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 135d92b..89ecd5f1c 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -77,7 +77,7 @@
void onCaptureStarted(RequestHolder holder, long timestamp);
void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
void onRequestQueueEmpty();
- void onRepeatingRequestError(long lastFrameNumber);
+ void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId);
}
/**
@@ -208,12 +208,14 @@
* <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
*
* @param lastFrameNumber Frame number of the last repeating request before it is stopped.
+ * @param repeatingRequestId The ID of the repeating request being stopped
*/
- public synchronized void setRepeatingRequestError(final long lastFrameNumber) {
+ public synchronized void setRepeatingRequestError(final long lastFrameNumber,
+ final int repeatingRequestId) {
mCurrentHandler.post(new Runnable() {
@Override
public void run() {
- mCurrentListener.onRepeatingRequestError(lastFrameNumber);
+ mCurrentListener.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
}
});
}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index d8df9a0..49d4096 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -264,10 +264,10 @@
}
@Override
- public void onRepeatingRequestError(long lastFrameNumber) {
+ public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
+ Object[] objArray = new Object[] { lastFrameNumber, repeatingRequestId };
Message msg = getHandler().obtainMessage(REPEATING_REQUEST_ERROR,
- /*arg1*/ (int) (lastFrameNumber & 0xFFFFFFFFL),
- /*arg2*/ (int) ( (lastFrameNumber >> 32) & 0xFFFFFFFFL));
+ /*obj*/ objArray);
getHandler().sendMessage(msg);
}
@@ -329,9 +329,10 @@
break;
}
case REPEATING_REQUEST_ERROR: {
- long lastFrameNumber = msg.arg2 & 0xFFFFFFFFL;
- lastFrameNumber = (lastFrameNumber << 32) | (msg.arg1 & 0xFFFFFFFFL);
- mCallbacks.onRepeatingRequestError(lastFrameNumber);
+ Object[] objArray = (Object[]) msg.obj;
+ long lastFrameNumber = (Long) objArray[0];
+ int repeatingRequestId = (Integer) objArray[1];
+ mCallbacks.onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
break;
}
case REQUEST_QUEUE_EMPTY: {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 621ea84..cb59fd1 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -263,7 +263,8 @@
}
@Override
- public void onRepeatingRequestError(final long lastFrameNumber) {
+ public void onRepeatingRequestError(final long lastFrameNumber,
+ final int repeatingRequestId) {
mResultHandler.post(new Runnable() {
@Override
public void run() {
@@ -271,7 +272,8 @@
Log.d(TAG, "doing onRepeatingRequestError callback.");
}
try {
- mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber);
+ mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber,
+ repeatingRequestId);
} catch (RemoteException e) {
throw new IllegalStateException(
"Received remote exception during onRepeatingRequestError " +
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 565a43e..aaf07e6 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -939,7 +939,8 @@
Log.d(TAG, "Stopped repeating request. Last frame number is " +
lastFrameNumber);
}
- mDeviceState.setRepeatingRequestError(lastFrameNumber);
+ mDeviceState.setRepeatingRequestError(lastFrameNumber,
+ burstHolder.getRequestId());
}
if (DEBUG) {
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 1dcaef4..3e08dcf 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -51,6 +51,7 @@
* Settings screen).
* </ol>
*
+ * <a name="BasicUsage"></a>
* <h3>Basic usage</h3>
*
* <p>The basic autofill process is defined by the workflow below:
@@ -122,12 +123,14 @@
* each {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} received - if it
* doesn't, the request will eventually time out and be discarded by the Android System.
*
+ * <a name="SavingUserData"></a>
* <h3>Saving user data</h3>
*
* <p>If the service is also interested on saving the data filled by the user, it must set a
* {@link SaveInfo} object in the {@link FillResponse}. See {@link SaveInfo} for more details and
* examples.
*
+ * <a name="UserAuthentication"></a>
* <h3>User authentication</h3>
*
* <p>The service can provide an extra degree of security by requiring the user to authenticate
@@ -164,6 +167,7 @@
* credentials in "vaults": the first response would contain fake datasets with the vault names,
* and the subsequent response would contain the app credentials stored in that vault.
*
+ * <a name="DataPartioning"></a>
* <h3>Data partitioning</h3>
*
* <p>The autofillable views in a screen should be grouped in logical groups called "partitions".
@@ -243,6 +247,7 @@
* <p>When the service returns multiple {@link FillResponse}, the last one overrides the previous;
* that's why the {@link SaveInfo} in the 2nd request above has the info for both partitions.
*
+ * <a name="PackageVerification"></a>
* <h3>Package verification</h3>
*
* <p>When autofilling app-specific data (like username and password), the service must verify
@@ -270,9 +275,16 @@
* }
* return hash.toString();
* }
- *
* </pre>
*
+ * <p>If the service did not store the signing certificates data the first time the data was saved
+ * — for example, because the data was created by a previous version of the app that did not
+ * use the Autofill Framework — the service should warn the user that the authenticity of the
+ * app cannot be confirmed (see an example on how to show such warning in the
+ * <a href="#WebSecurityDisclaimer">Web security</a> section below), and if the user agrees,
+ * then the service could save the data from the signing ceriticates for future use.
+ *
+ * <a name="IgnoringViews"></a>
* <h3>Ignoring views</h3>
*
* <p>If the service find views that cannot be autofilled (for example, a text field representing
@@ -281,6 +293,7 @@
* a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
* focused.
*
+ * <a name="WebSecurity"></a>
* <h3>Web security</h3>
*
* <p>When handling autofill requests that represent web pages (typically
@@ -313,6 +326,7 @@
* }
* </pre>
*
+ * <a name="WebSecurityDisclaimer"></a>
* <p>If the association between the web domain and app package cannot be verified through the steps
* above, but the service thinks that it is appropriate to fill persisted credentials that are
* stored for the web domain, the service should warn the user about the potential data
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index 8ab856e..2413e97 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -86,7 +86,7 @@
}
try {
final Matcher matcher = field.first.matcher(value);
- if (!matcher.matches()) {
+ if (!matcher.find()) {
if (sDebug) Log.d(TAG, "match for " + field.first + " failed on id " + id);
return;
}
diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl
index 682e7ee..d5e10d6 100644
--- a/core/java/android/service/oemlock/IOemLockService.aidl
+++ b/core/java/android/service/oemlock/IOemLockService.aidl
@@ -28,7 +28,6 @@
void setOemUnlockAllowedByUser(boolean allowed);
boolean isOemUnlockAllowedByUser();
- boolean canUserAllowOemUnlock();
boolean isOemUnlockAllowed();
boolean isDeviceOemUnlocked();
}
diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java
index 3a56d9f..f0d6603 100644
--- a/core/java/android/service/oemlock/OemLockManager.java
+++ b/core/java/android/service/oemlock/OemLockManager.java
@@ -118,24 +118,6 @@
}
/**
- * Returns whether all parties other than the user allow OEM unlock meaning the user can
- * directly control whether or not the device can be OEM unlocked.
- *
- * If this is true, {@link #isOemUnlockAllowedByUser} is the same as {@link #isOemUnlockAllowed}
- *
- * @return Whether the user can directly control whether the device can be OEM unlocked.
- *
- * @hide
- */
- public boolean canUserAllowOemUnlock() {
- try {
- return mService.canUserAllowOemUnlock();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* @return Whether the bootloader is able to OEM unlock the device.
*
* @hide
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 969608b..4e06577 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -17,6 +17,9 @@
package com.android.internal.policy;
import android.graphics.Outline;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.Pair;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -1103,8 +1106,8 @@
boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
- mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge || navBarToLeftEdge,
- navBarToLeftEdge,
+ mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
+ navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
0 /* sideInset */, animate && !disallowAnimate, false /* force */);
boolean statusBarNeedsRightInset = navBarToRightEdge
@@ -1114,7 +1117,7 @@
int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
: statusBarNeedsLeftInset ? mLastLeftInset : 0;
updateColorViewInt(mStatusColorViewState, sysUiVisibility,
- calculateStatusBarColor(), mLastTopInset,
+ calculateStatusBarColor(), 0, mLastTopInset,
false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
animate && !disallowAnimate,
mForceWindowDrawsStatusBarBackground);
@@ -1201,6 +1204,7 @@
* @param state the color view to update.
* @param sysUiVis the current systemUiVisibility to apply.
* @param color the current color to apply.
+ * @param dividerColor the current divider color to apply.
* @param size the current size in the non-parent-matching dimension.
* @param verticalBar if true the view is attached to a vertical edge, otherwise to a
* horizontal edge,
@@ -1208,7 +1212,7 @@
* @param animate if true, the change will be animated.
*/
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
- int size, boolean verticalBar, boolean seascape, int sideMargin,
+ int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
boolean animate, boolean force) {
state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
@@ -1227,7 +1231,7 @@
if (view == null) {
if (showView) {
state.view = view = new View(mContext);
- view.setBackgroundColor(color);
+ setColor(view, color, dividerColor, verticalBar, seascape);
view.setTransitionName(state.attributes.transitionName);
view.setId(state.attributes.id);
visibilityChanged = true;
@@ -1262,7 +1266,7 @@
view.setLayoutParams(lp);
}
if (showView) {
- view.setBackgroundColor(color);
+ setColor(view, color, dividerColor, verticalBar, seascape);
}
}
if (visibilityChanged) {
@@ -1295,6 +1299,34 @@
state.color = color;
}
+ private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
+ boolean seascape) {
+ if (dividerColor != 0) {
+ final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
+ if (dir == null || dir.first != verticalBar || dir.second != seascape) {
+ final int size = Math.round(
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
+ v.getContext().getResources().getDisplayMetrics()));
+ // Use an inset to make the divider line on the side that faces the app.
+ final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
+ verticalBar && !seascape ? size : 0,
+ !verticalBar ? size : 0,
+ verticalBar && seascape ? size : 0, 0);
+ v.setBackground(new LayerDrawable(new Drawable[] {
+ new ColorDrawable(dividerColor), d }));
+ v.setTag(new Pair<>(verticalBar, seascape));
+ } else {
+ final LayerDrawable d = (LayerDrawable) v.getBackground();
+ final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
+ ((ColorDrawable) inset.getDrawable()).setColor(color);
+ ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
+ }
+ } else {
+ v.setTag(null);
+ v.setBackgroundColor(color);
+ }
+ }
+
private void updateColorViewTranslations() {
// Put the color views back in place when they get moved off the screen
// due to the the ViewRootImpl panning.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 57b0a73..5f1932c 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -233,6 +233,7 @@
private int mTextColor = 0;
int mStatusBarColor = 0;
int mNavigationBarColor = 0;
+ int mNavigationBarDividerColor = 0;
private boolean mForcedStatusBarColor = false;
private boolean mForcedNavigationBarColor = false;
@@ -2432,6 +2433,8 @@
}
if (!mForcedNavigationBarColor) {
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
+ mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
+ 0x00000000);
}
WindowManager.LayoutParams params = getAttributes();
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 12d707b..105ef44 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2056,6 +2056,14 @@
Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. -->
<attr name="navigationBarColor" format="color" />
+ <!-- @hide
+ Shows 1dp line of the specified color between the navigation bar and the app content.
+ <p>For this to take effect, the window must be drawing the system bar backgrounds with
+ {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
+ have been requested to be translucent with
+ {@link android.R.attr#windowTranslucentNavigation}. -->
+ <attr name="navigationBarDividerColor" format="color" />
+
<!-- The duration, in milliseconds, of the window background fade duration
when transitioning into or away from an Activity when called with an
Activity Transition. Corresponds to
@@ -2088,6 +2096,16 @@
{@link android.R.attr#windowBackground}.
-->
<attr name="windowSplashscreenContent" format="reference" />
+
+ <!-- @hide If set, the navigation bar will be drawn such that it is
+ compatible with a light navigation bar background.
+ <p>For this to take effect, the window must be drawing the system bar backgrounds with
+ {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
+ have been requested to be translucent with
+ {@link android.R.attr#windowTranslucentNavigation}.
+ Corresponds to setting {@link android.view.View#SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR} on
+ the decor view. -->
+ <attr name="windowLightNavigationBar" format="boolean" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 8755e37..bf0c906 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -745,6 +745,11 @@
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorEdgeEffect">@android:color/black</item>
+
+ <!-- Add white nav bar with divider that matches material -->
+ <item name="navigationBarDividerColor">#1f000000</item>
+ <item name="navigationBarColor">@android:color/white</item>
+ <item name="windowLightNavigationBar">true</item>
</style>
<!-- @hide DeviceDefault theme for a window that should use Settings theme colors
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
index c707240..a8d5164 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
@@ -275,6 +275,21 @@
}
@Test
+ public void testStepLoop() {
+ openTuner();
+
+ for (int i = 0; i < 10; i++) {
+ Log.d(TAG, "step loop iteration " + (i + 1));
+
+ int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
+ assertEquals(RadioManager.STATUS_OK, ret);
+ verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
+
+ resetCallback();
+ }
+ }
+
+ @Test
public void testTuneAndGetPI() {
openTuner();
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2d008c7..022198b 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -137,7 +137,7 @@
mLooper->start(
false, // runOnCallingThread
true, // canCallJava
- PRIORITY_FOREGROUND);
+ ANDROID_PRIORITY_VIDEO);
if (nameIsType) {
mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index fcb861c..cf5882f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -271,7 +271,7 @@
* @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
*/
@Override
- public void onRepeatingRequestError(long lastFrameNumber) {
+ public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
// TODO Auto-generated method stub
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 476f016..e628b68 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -161,7 +161,7 @@
* @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
*/
@Override
- public void onRepeatingRequestError(long lastFrameNumber) {
+ public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
// TODO Auto-generated method stub
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index bf1bf34..40c2b1f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -339,26 +339,35 @@
synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(userId).get(packageName);
if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
- mBackgroundHandler.post(() -> {
- try {
- final StorageStats stats = mStats.queryStatsForPackage(
- entry.info.storageUuid, packageName, UserHandle.of(userId));
- final PackageStats legacy = new PackageStats(packageName, userId);
- legacy.codeSize = stats.getCodeBytes();
- legacy.dataSize = stats.getDataBytes();
- legacy.cacheSize = stats.getCacheBytes();
- try {
- mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true);
- } catch (RemoteException ignored) {
- }
- } catch (NameNotFoundException | IOException e) {
- Log.w(TAG, "Failed to query stats: " + e);
- try {
- mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false);
- } catch (RemoteException ignored) {
- }
- }
- });
+ mBackgroundHandler.post(
+ () -> {
+ try {
+ final StorageStats stats =
+ mStats.queryStatsForPackage(
+ entry.info.storageUuid,
+ packageName,
+ UserHandle.of(userId));
+ final long cacheQuota =
+ mStats.getCacheQuotaBytes(
+ entry.info.storageUuid.toString(), entry.info.uid);
+ final PackageStats legacy = new PackageStats(packageName, userId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota);
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
+ legacy, true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(
+ null, false);
+ } catch (RemoteException ignored) {
+ }
+ }
+ });
}
if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
}
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index b27dedd..2148c80 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -59,6 +59,7 @@
<item type="id" name="transformation_start_y_tag"/>
<item type="id" name="transformation_start_scale_x_tag"/>
<item type="id" name="transformation_start_scale_y_tag"/>
+ <item type="id" name="continuous_clipping_tag"/>
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 017365f..176112b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -31,6 +31,7 @@
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
+import android.text.format.DateUtils;
import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -51,6 +52,8 @@
public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
public static final String PREFIX = "custom(";
+ private static final long CUSTOM_STALE_TIMEOUT = DateUtils.HOUR_IN_MILLIS;
+
private static final boolean DEBUG = false;
// We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
@@ -83,6 +86,11 @@
mUser = ActivityManager.getCurrentUser();
}
+ @Override
+ protected long getStaleTimeout() {
+ return CUSTOM_STALE_TIMEOUT + DateUtils.MINUTE_IN_MILLIS * mHost.indexOf(getTileSpec());
+ }
+
private void setTileIcon() {
try {
PackageManager pm = mContext.getPackageManager();
@@ -186,7 +194,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8c04daf..6a5530f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -32,10 +32,12 @@
import android.os.Looper;
import android.os.Message;
import android.service.quicksettings.Tile;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.Utils;
@@ -45,7 +47,6 @@
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.PagedTileLayout;
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSHost;
@@ -62,14 +63,18 @@
protected final String TAG = "Tile." + getClass().getSimpleName();
protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
+ private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
+
protected final QSHost mHost;
protected final Context mContext;
- protected final H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
+ // @NonFinalForTesting
+ protected H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
private final ArraySet<Object> mListeners = new ArraySet<>();
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Object mStaleListener = new Object();
protected TState mState = newTileState();
private TState mTmpState = newTileState();
private boolean mAnnounceNextStateChange;
@@ -95,6 +100,7 @@
protected QSTileImpl(QSHost host) {
mHost = host;
mContext = host.getContext();
+ handleStale(); // Tile was just created, must be stale.
}
/**
@@ -106,6 +112,7 @@
if (mListeners.add(listener) && mListeners.size() == 1) {
if (DEBUG) Log.d(TAG, "setListening " + true);
mHandler.obtainMessage(H.SET_LISTENING, 1, 0).sendToTarget();
+ refreshState(); // Ensure we get at least one refresh after listening.
}
} else {
if (mListeners.remove(listener) && mListeners.size() == 0) {
@@ -115,6 +122,15 @@
}
}
+ protected long getStaleTimeout() {
+ return DEFAULT_STALE_TIMEOUT;
+ }
+
+ @VisibleForTesting
+ protected void handleStale() {
+ setListening(mStaleListener, true);
+ }
+
public String getTileSpec() {
return mTileSpec;
}
@@ -273,6 +289,9 @@
if (changed) {
handleStateChanged();
}
+ mHandler.removeMessages(H.STALE);
+ mHandler.sendEmptyMessageDelayed(H.STALE, getStaleTimeout());
+ setListening(mStaleListener, false);
}
private void handleStateChanged() {
@@ -326,11 +345,11 @@
handleRefreshState(null);
}
- protected abstract void setListening(boolean listening);
+ protected abstract void handleSetListening(boolean listening);
protected void handleDestroy() {
if (mListeners.size() != 0) {
- setListening(false);
+ handleSetListening(false);
}
mCallbacks.clear();
}
@@ -380,8 +399,10 @@
private static final int REMOVE_CALLBACKS = 12;
private static final int REMOVE_CALLBACK = 13;
private static final int SET_LISTENING = 14;
+ private static final int STALE = 15;
- private H(Looper looper) {
+ @VisibleForTesting
+ protected H(Looper looper) {
super(looper);
}
@@ -436,8 +457,11 @@
name = "handleClearState";
handleClearState();
} else if (msg.what == SET_LISTENING) {
- name = "setListening";
- setListening(msg.arg1 != 0);
+ name = "handleSetListening";
+ handleSetListening(msg.arg1 != 0);
+ } else if (msg.what == STALE) {
+ name = "handleStale";
+ handleStale();
} else {
throw new IllegalArgumentException("Unknown msg: " + msg.what);
}
@@ -515,7 +539,7 @@
}
}
- protected class AnimationIcon extends ResourceIcon {
+ protected static class AnimationIcon extends ResourceIcon {
private final int mAnimatedResId;
public AnimationIcon(int resId, int staticResId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2e7012e..bef1aff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -110,7 +110,7 @@
}
}
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 3f419a8..95504ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -55,7 +55,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mBatteryController.addCallback(this);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index bc6233d..774f0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -76,7 +76,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mController.addCallback(mCallback);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 2fc9fc7..fb396b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -91,9 +91,9 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (mController == null) return;
- if (DEBUG) Log.d(TAG, "setListening " + listening);
+ if (DEBUG) Log.d(TAG, "handleSetListening " + listening);
if (listening) {
mController.addCallback(mCallback);
mKeyguard.addCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index f900da0..0bb7479 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -92,7 +92,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mController.addCallback(mSignalCallback);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 40fe484..b93f1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -63,7 +63,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
mSetting.setListening(listening);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 8b62beb..a102696 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -45,7 +45,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mDataSaverController.addCallback(this);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 5938749..9e265e22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -232,7 +232,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
if (mListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index e6ac908..f2ead1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -54,7 +54,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mFlashlightController.addCallback(this);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index c17573d..910b6b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -74,7 +74,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 00cfbfa..4f4004c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -76,7 +76,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 5e66334..c35f591 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -55,7 +55,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mController.addCallback(mCallback);
mKeyguard.addCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 6500740..b3ff4e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -51,7 +51,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
mListening = listening;
if (mListening) {
mContext.registerReceiver(mNfcReceiver,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 2a12769..4c20361 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -95,7 +95,7 @@
}
@Override
- protected void setListening(boolean listening) {
+ protected void handleSetListening(boolean listening) {
mIsListening = listening;
if (listening) {
mController.setListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index fb937bd..1e00894 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -62,7 +62,7 @@
return new BooleanState();
}
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (mController == null) return;
if (listening) {
mController.addCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index d6043f4..bde1c98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -69,7 +69,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mUserInfoController.addCallback(this);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 136cf21..33b1512 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -75,7 +75,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mController.addCallback(mSignalCallback);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 38821f6..5f7d6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -48,7 +48,7 @@
}
@Override
- public void setListening(boolean listening) {
+ public void handleSetListening(boolean listening) {
if (listening) {
mProfileController.addCallback(this);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index f844866..f545556 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -65,6 +65,7 @@
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
@@ -119,6 +120,7 @@
private boolean mFinishedOnStartup;
private boolean mIgnoreAltTabRelease;
private boolean mIsVisible;
+ private boolean mRecentsStartRequested;
private Configuration mLastConfig;
// Top level views
@@ -416,6 +418,7 @@
launchState.launchedFromHome = false;
onEnterAnimationComplete();
}
+ mRecentsStartRequested = false;
}
@Override
@@ -449,7 +452,6 @@
* Reloads the stack views upon launching Recents.
*/
private void reloadStackView() {
-
// If the Recents component has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -572,7 +574,9 @@
MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(false);
- if (!isChangingConfigurations()) {
+ // When recents starts again before onStop, do not reset launch flags so entrance animation
+ // can run
+ if (!isChangingConfigurations() && !mRecentsStartRequested) {
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
// wait on the system to send a signal that was never queued.
@@ -718,6 +722,10 @@
MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
}
+ public final void onBusEvent(RecentsActivityStartingEvent event) {
+ mRecentsStartRequested = true;
+ }
+
public final void onBusEvent(UserInteractionEvent event) {
// Stop the fast-toggle dozer
mIterateTrigger.stopDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 5eefe9a..184b95d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -22,10 +22,12 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.systemui.Interpolators;
@@ -53,6 +55,7 @@
SystemProperties.getBoolean("debug.icon_opening_animations", true);
private static final boolean ICON_ANMATIONS_WHILE_SCROLLING
= SystemProperties.getBoolean("debug.icon_scroll_animations", true);
+ private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
private ViewInvertHelper mViewInvertHelper;
private boolean mDark;
private NotificationIconContainer mShelfIcons;
@@ -305,6 +308,16 @@
mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
mShelfIcons.calculateIconTranslations();
mShelfIcons.applyIconStates();
+ for (int i = 0; i < mHostLayout.getChildCount(); i++) {
+ View child = mHostLayout.getChildAt(i);
+ if (!(child instanceof ExpandableNotificationRow)
+ || child.getVisibility() == GONE) {
+ continue;
+ }
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ updateIconClipAmount(row);
+ updateContinuousClipping(row);
+ }
boolean hideBackground = numViewsInShelf < 1.0f;
setHideBackground(hideBackground || backgroundForceHidden);
if (mNotGoneIndex == -1) {
@@ -312,6 +325,43 @@
}
}
+ private void updateIconClipAmount(ExpandableNotificationRow row) {
+ float maxTop = row.getTranslationY();
+ StatusBarIconView icon = row.getEntry().expandedIcon;
+ float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
+ if (shelfIconPosition < maxTop) {
+ int top = (int) (maxTop - shelfIconPosition);
+ Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
+ icon.setClipBounds(clipRect);
+ } else {
+ icon.setClipBounds(null);
+ }
+ }
+
+ private void updateContinuousClipping(final ExpandableNotificationRow row) {
+ StatusBarIconView icon = row.getEntry().expandedIcon;
+ boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
+ boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
+ if (needsContinuousClipping && !isContinuousClipping) {
+ ViewTreeObserver.OnPreDrawListener predrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ boolean animatingY = ViewState.isAnimatingY(icon);
+ if (!animatingY || !icon.isAttachedToWindow()) {
+ icon.getViewTreeObserver().removeOnPreDrawListener(this);
+ icon.setTag(TAG_CONTINUOUS_CLIPPING, null);
+ return true;
+ }
+ updateIconClipAmount(row);
+ return true;
+ }
+ };
+ icon.getViewTreeObserver().addOnPreDrawListener(predrawListener);
+ icon.setTag(TAG_CONTINUOUS_CLIPPING, predrawListener);
+ }
+ }
+
private void updateNotificationClipHeight(ExpandableNotificationRow row,
float notificationClipEnd) {
float viewEnd = row.getTranslationY() + row.getActualHeight();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index d823981..004ff29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -25,10 +25,15 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.lang.Thread.sleep;
+
import android.content.Intent;
import android.metrics.LogMaker;
import android.support.test.filters.SmallTest;
@@ -67,10 +72,10 @@
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mHost = mock(QSTileHost.class);
when(mHost.indexOf(spec)).thenReturn(POSITION);
- mTestableLooper.runWithLooper(() -> {
- mTile = new TileImpl(mHost);
- mTile.setTileSpec(spec);
- });
+
+ mTile = spy(new TileImpl(mHost));
+ mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
+ mTile.setTileSpec(spec);
}
@Test
@@ -102,6 +107,31 @@
verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
}
+ @Test
+ public void testStaleTimeout() throws InterruptedException {
+ when(mTile.getStaleTimeout()).thenReturn(5l);
+ clearInvocations(mTile);
+
+ mTile.handleRefreshState(null);
+ mTestableLooper.processAllMessages();
+ verify(mTile, never()).handleStale();
+
+ sleep(10);
+ mTestableLooper.processAllMessages();
+ verify(mTile).handleStale();
+ }
+
+ @Test
+ public void testStaleListening() {
+ mTile.handleStale();
+ mTestableLooper.processAllMessages();
+ verify(mTile).handleSetListening(eq(true));
+
+ mTile.handleRefreshState(null);
+ mTestableLooper.processAllMessages();
+ verify(mTile).handleSetListening(eq(false));
+ }
+
private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
private final int mCategory;
@@ -166,7 +196,7 @@
}
@Override
- protected void setListening(boolean listening) {
+ protected void handleSetListening(boolean listening) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
index 12a4399..f8fd53d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
@@ -185,7 +185,9 @@
countDownLatch.await();
}
+ /* Cancelling requires us to be on the UI thread otherwise we might have a race */
@Test
+ @UiThreadTest
public void testSupersedesExistingTask() throws Exception {
mNotificationInflater.inflateNotificationViews();
mNotificationInflater.setIsLowPriority(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index a915cb20..b22a646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -27,7 +27,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
@@ -145,7 +144,6 @@
@Test
@RunWithLooper
public void testSortOrder() {
- Log.d("TestTest", "Config " + mContext.getResources().getConfiguration().uiMode);
Object def = new Object();
Object uiMode = new Object();
Object tuner = new Object();
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index eb895de..8156025 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4305,6 +4305,12 @@
// OS: O MR
ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES = 1096;
+ // FIELD - Whether smart suggestion ranking was enabled or not
+ // Type: int encoded boolean
+ // CATEGORY: SETTINGS
+ // OS: O MR
+ FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED = 1097;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 24f3b33..371e74d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -229,6 +229,13 @@
public void setFilterText(@Nullable String filterText) {
throwIfDestroyed();
if (mAdapter == null) {
+ // ViewState doesn't not support filtering - typically when it's for an authenticated
+ // FillResponse.
+ if (TextUtils.isEmpty(filterText)) {
+ mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+ } else {
+ mCallback.requestHideFillUi();
+ }
return;
}
@@ -510,8 +517,9 @@
final ViewItem item = mAllItems.get(i);
final String value = item.getValue();
// No value, i.e. null, matches any filter
- if (value == null
- || value.toLowerCase().startsWith(constraintLowerCase)) {
+ if ((value == null && item.mDataset.getAuthentication() == null)
+ || (value != null
+ && value.toLowerCase().startsWith(constraintLowerCase))) {
filteredItems.add(item);
}
}
@@ -525,9 +533,11 @@
final boolean resultCountChanged;
final int oldItemCount = mFilteredItems.size();
mFilteredItems.clear();
- @SuppressWarnings("unchecked")
- final List<ViewItem> items = (List<ViewItem>) results.values;
- mFilteredItems.addAll(items);
+ if (results.count > 0) {
+ @SuppressWarnings("unchecked")
+ final List<ViewItem> items = (List<ViewItem>) results.values;
+ mFilteredItems.addAll(items);
+ }
resultCountChanged = (oldItemCount != mFilteredItems.size());
if (resultCountChanged) {
announceSearchResultIfNeeded();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 791b2c0..56ae57b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1214,6 +1214,11 @@
// Still waiting for something to pause; can't sleep yet.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
shouldSleep = false;
+ } else if (mLastPausedActivity == topActivity()) {
+ // Our top activity is currently paused, we need to ensure we move it to the stopped
+ // state.
+ stopActivityLocked(mLastPausedActivity);
+ shouldSleep = false;
}
if (!shuttingDown) {
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 5e19b13..40c6639 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -149,8 +149,12 @@
final long token = Binder.clearCallingIdentity();
try {
- if (!canUserAllowOemUnlock()) {
- throw new SecurityException("User cannot allow OEM unlock");
+ if (!isOemUnlockAllowedByAdmin()) {
+ throw new SecurityException("Admin does not allow OEM unlock");
+ }
+
+ if (!mOemLock.isOemUnlockAllowedByCarrier()) {
+ throw new SecurityException("Carrier does not allow OEM unlock");
}
mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
@@ -172,18 +176,6 @@
}
@Override
- public boolean canUserAllowOemUnlock() {
- enforceOemUnlockReadPermission();
-
- final long token = Binder.clearCallingIdentity();
- try {
- return isOemUnlockAllowedByAdmin() && mOemLock.isOemUnlockAllowedByCarrier();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public boolean isOemUnlockAllowed() {
enforceOemUnlockReadPermission();
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 698d387..0f580d8 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -147,8 +147,13 @@
// Get the class loader context dependencies.
// For each code path in the package, this array contains the class loader context that
// needs to be passed to dexopt in order to ensure correct optimizations.
+ boolean[] pathsWithCode = new boolean[paths.size()];
+ pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ for (int i = 1; i < paths.size(); i++) {
+ pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ }
String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
- pkg.applicationInfo, sharedLibraries);
+ pkg.applicationInfo, sharedLibraries, pathsWithCode);
// Sanity check that we do not call dexopt with inconsistent data.
if (paths.size() != classLoaderContexts.length) {
@@ -164,10 +169,15 @@
int result = DEX_OPT_SKIPPED;
for (int i = 0; i < paths.size(); i++) {
// Skip paths that have no code.
- if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) ||
- (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) {
+ if (!pathsWithCode[i]) {
continue;
}
+ if (classLoaderContexts[i] == null) {
+ throw new IllegalStateException("Inconsistent information in the "
+ + "package structure. A split is marked to contain code "
+ + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
+ }
+
// Append shared libraries with split dependencies for this split.
String path = paths.get(i);
if (options.getSplitName() != null) {
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 0196212..ef2ee4a 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -35,7 +35,9 @@
/**
* Creates the class loader context dependencies for each of the application code paths.
* The returned array contains the class loader contexts that needs to be passed to dexopt in
- * order to ensure correct optimizations.
+ * order to ensure correct optimizations. "Code" paths with no actual code, as specified by
+ * {@param pathsWithCode}, are ignored and will have null as their context in the returned array
+ * (configuration splits are an example of paths without code).
*
* A class loader context describes how the class loader chain should be built by dex2oat
* in order to ensure that classes are resolved during compilation as they would be resolved
@@ -60,7 +62,8 @@
* {@link android.app.LoadedApk#makePaths(
* android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
*/
- public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries) {
+ public static String[] getClassLoaderContexts(ApplicationInfo info,
+ String[] sharedLibraries, boolean[] pathsWithCode) {
// The base class loader context contains only the shared library.
String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
String baseApkContextClassLoader = encodeClassLoader(
@@ -86,7 +89,7 @@
// Index 0 is the class loaded context for the base apk.
// Index `i` is the class loader context encoding for split `i`.
String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
- classLoaderContexts[0] = baseApkContextClassLoader;
+ classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) {
// If the app didn't request for the splits to be loaded in isolation or if it does not
@@ -94,7 +97,15 @@
// apk class loader (in the order of their definition).
String classpath = sharedLibrariesAndBaseClassPath;
for (int i = 1; i < classLoaderContexts.length; i++) {
- classLoaderContexts[i] = encodeClassLoader(classpath, info.classLoaderName);
+ classLoaderContexts[i] = pathsWithCode[i]
+ ? encodeClassLoader(classpath, info.classLoaderName) : null;
+ // Note that the splits with no code are not removed from the classpath computation.
+ // i.e. split_n might get the split_n-1 in its classpath dependency even
+ // if split_n-1 has no code.
+ // The splits with no code do not matter for the runtime which ignores
+ // apks without code when doing the classpath checks. As such we could actually
+ // filter them but we don't do it in order to keep consistency with how the apps
+ // are loaded.
classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]);
}
} else {
@@ -116,9 +127,17 @@
String splitDependencyOnBase = encodeClassLoader(
sharedLibrariesAndBaseClassPath, info.classLoaderName);
SparseArray<int[]> splitDependencies = info.splitDependencies;
+
+ // Note that not all splits have dependencies (e.g. configuration splits)
+ // The splits without dependencies will have classLoaderContexts[config_split_index]
+ // set to null after this step.
for (int i = 1; i < splitDependencies.size(); i++) {
- getParentDependencies(splitDependencies.keyAt(i), splitClassLoaderEncodingCache,
- splitDependencies, classLoaderContexts, splitDependencyOnBase);
+ int splitIndex = splitDependencies.keyAt(i);
+ if (pathsWithCode[splitIndex]) {
+ // Compute the class loader context only for the splits with code.
+ getParentDependencies(splitIndex, splitClassLoaderEncodingCache,
+ splitDependencies, classLoaderContexts, splitDependencyOnBase);
+ }
}
// At this point classLoaderContexts contains only the parent dependencies.
@@ -126,8 +145,17 @@
// come first in the context.
for (int i = 1; i < classLoaderContexts.length; i++) {
String splitClassLoader = encodeClassLoader("", info.splitClassLoaderNames[i - 1]);
- classLoaderContexts[i] = encodeClassLoaderChain(
- splitClassLoader, classLoaderContexts[i]);
+ if (pathsWithCode[i]) {
+ // If classLoaderContexts[i] is null it means that the split does not have
+ // any dependency. In this case its context equals its declared class loader.
+ classLoaderContexts[i] = classLoaderContexts[i] == null
+ ? splitClassLoader
+ : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+ } else {
+ // This is a split without code, it has no dependency and it is not compiled.
+ // Its context will be null.
+ classLoaderContexts[i] = null;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index dd887e0..64ac7ac 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1657,7 +1657,14 @@
if (mWin.mAppToken != null) {
mWin.mAppToken.setCanTurnScreenOn(false);
}
- mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
+
+ // We do not add {@code SET_TURN_ON_SCREEN} when the screen is already
+ // interactive as the value may persist until the next animation, which could
+ // potentially occurring while turning off the screen. This would lead to the
+ // screen incorrectly turning back on.
+ if (!mService.mPowerManager.isInteractive()) {
+ mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
+ }
}
}
if (hasSurface()) {
diff --git a/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp b/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
index 85ec9e0..81d46f3 100644
--- a/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
+++ b/services/core/jni/BroadcastRadio/NativeCallbackThread.cpp
@@ -48,15 +48,19 @@
return;
}
- while (!mExiting) {
- ALOGV("Waiting for task...");
+ while (true) {
Task task;
{
unique_lock<mutex> lk(mQueueMutex);
- mQueueCond.wait(lk);
- if (mExiting) break;
- if (mQueue.empty()) continue;
+ if (mExiting) break;
+ if (mQueue.empty()) {
+ ALOGV("Waiting for task...");
+ mQueueCond.wait(lk);
+ if (mExiting) break;
+ if (mQueue.empty()) continue;
+ }
+
task = mQueue.front();
mQueue.pop();
}
@@ -74,6 +78,7 @@
ALOGE_IF(res != JNI_OK, "Couldn't detach thread");
ALOGV("Native callback thread %p finished", this);
+ ALOGD_IF(!mQueue.empty(), "Skipped execution of %zu tasks", mQueue.size());
}
void NativeCallbackThread::enqueue(const Task &task) {
@@ -84,6 +89,7 @@
return;
}
+ ALOGV("Adding task to the queue...");
mQueue.push(task);
mQueueCond.notify_one();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 2c56a82..c8c8eb1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -46,22 +46,35 @@
private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
DelegateLastClassLoader.class.getName();
- private ApplicationInfo createMockApplicationInfo(String baseClassLoader, boolean addSplits,
+ private static class TestData {
+ ApplicationInfo info;
+ boolean[] pathsWithCode;
+ }
+
+ private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
boolean addSplitDependencies) {
ApplicationInfo ai = new ApplicationInfo();
String codeDir = "/data/app/mock.android.com";
ai.setBaseCodePath(codeDir + "/base.dex");
ai.classLoaderName = baseClassLoader;
ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+ boolean[] pathsWithCode;
+ if (!addSplits) {
+ pathsWithCode = new boolean[] {true};
+ } else {
+ pathsWithCode = new boolean[9];
+ Arrays.fill(pathsWithCode, true);
+ pathsWithCode[7] = false; // config split
- if (addSplits) {
ai.setSplitCodePaths(new String[]{
codeDir + "/base-1.dex",
codeDir + "/base-2.dex",
codeDir + "/base-3.dex",
codeDir + "/base-4.dex",
codeDir + "/base-5.dex",
- codeDir + "/base-6.dex"});
+ codeDir + "/base-6.dex",
+ codeDir + "/config-split-7.dex",
+ codeDir + "/feature-no-deps.dex"});
ai.splitClassLoaderNames = new String[]{
DELEGATE_LAST_CLASS_LOADER_NAME,
@@ -69,7 +82,9 @@
PATH_CLASS_LOADER_NAME,
DEX_CLASS_LOADER_NAME,
PATH_CLASS_LOADER_NAME,
- null}; // A null class loader name should default to PathClassLoader.
+ null, // A null class loader name should default to PathClassLoader.
+ null, // The config split gets a null class loader.
+ null}; // The feature split with no dependency and no specified class loader.
if (addSplitDependencies) {
ai.splitDependencies = new SparseArray<>(ai.splitClassLoaderNames.length + 1);
ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency
@@ -79,18 +94,24 @@
ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base
ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base
ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
+ // Do not add the config split to the dependency list.
+ // Do not add the feature split with no dependency to the dependency list.
}
}
- return ai;
+ TestData data = new TestData();
+ data.info = ai;
+ data.pathsWithCode = pathsWithCode;
+ return data;
}
@Test
public void testSplitChain() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
contexts[1]);
@@ -99,15 +120,18 @@
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@Test
public void testSplitChainNoSplitDependencies() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
@@ -119,15 +143,21 @@
assertEquals(
"PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals(
+ "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ contexts[8]); // feature split with no dependency
}
@Test
public void testSplitChainNoIsolationNoSharedLibrary() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
- ai.privateFlags = ai.privateFlags & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+ data.info.privateFlags = data.info.privateFlags
+ & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[]", contexts[0]);
assertEquals("PCL[base.dex]", contexts[1]);
assertEquals("PCL[base.dex:base-1.dex]", contexts[2]);
@@ -137,14 +167,20 @@
assertEquals(
"PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals(
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ contexts[8]); // feature split with no dependency
}
+
@Test
public void testSplitChainNoSharedLibraries() {
- ApplicationInfo ai = createMockApplicationInfo(
+ TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, true, true);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("DLC[]", contexts[0]);
assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];DLC[base.dex]", contexts[1]);
assertEquals("DLC[];PCL[base-4.dex];DLC[base.dex]", contexts[2]);
@@ -152,16 +188,19 @@
assertEquals("PCL[];DLC[base.dex]", contexts[4]);
assertEquals("PCL[];DLC[base.dex]", contexts[5]);
assertEquals("PCL[];PCL[base-5.dex];DLC[base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@Test
public void testSplitChainWithNullPrimaryClassLoader() {
// A null classLoaderName should mean PathClassLoader.
- ApplicationInfo ai = createMockApplicationInfo(null, true, true);
+ TestData data = createMockApplicationInfo(null, true, true);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
@@ -169,13 +208,16 @@
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@Test
public void tesNoSplits() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -183,9 +225,10 @@
@Test
public void tesNoSplitsNullClassLoaderName() {
- ApplicationInfo ai = createMockApplicationInfo(null, false, false);
+ TestData data = createMockApplicationInfo(null, false, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -193,10 +236,11 @@
@Test
public void tesNoSplitDelegateLast() {
- ApplicationInfo ai = createMockApplicationInfo(
+ TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("DLC[a.dex:b.dex]", contexts[0]);
@@ -204,8 +248,9 @@
@Test
public void tesNoSplitsNoSharedLibraries() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[]", contexts[0]);
@@ -213,15 +258,55 @@
@Test
public void tesNoSplitDelegateLastNoSharedLibraries() {
- ApplicationInfo ai = createMockApplicationInfo(
+ TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("DLC[]", contexts[0]);
}
@Test
+ public void testContextWithNoCode() {
+ TestData data = createMockApplicationInfo(null, true, false);
+ Arrays.fill(data.pathsWithCode, false);
+
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals(null, contexts[0]);
+ assertEquals(null, contexts[1]);
+ assertEquals(null, contexts[2]);
+ assertEquals(null, contexts[3]);
+ assertEquals(null, contexts[4]);
+ assertEquals(null, contexts[5]);
+ assertEquals(null, contexts[6]);
+ assertEquals(null, contexts[7]);
+ }
+
+ @Test
+ public void testContextBaseNoCode() {
+ TestData data = createMockApplicationInfo(null, true, true);
+ data.pathsWithCode[0] = false;
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals(null, contexts[0]);
+ assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
+ assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]);
+ }
+
+ @Test
public void testProcessContextForDexLoad() {
List<String> classLoaders = Arrays.asList(
DELEGATE_LAST_CLASS_LOADER_NAME,
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index 4c3f7e7..1e8cf18 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -18,19 +18,19 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.FileInfo;
import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.IDownloadProgressListener;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
import android.telephony.mbms.MbmsDownloadManagerCallback;
import android.telephony.mbms.MbmsDownloadReceiver;
import android.telephony.mbms.MbmsException;
@@ -44,6 +44,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -52,147 +53,38 @@
public class MbmsDownloadManager {
private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName();
+ /** @hide */
+ // TODO: systemapi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
"android.telephony.action.EmbmsDownload";
- /**
- * The MBMS middleware should send this when a download of single file has completed or
- * failed. Mandatory extras are
- * {@link #EXTRA_RESULT}
- * {@link #EXTRA_FILE_INFO}
- * {@link #EXTRA_REQUEST}
- * {@link #EXTRA_TEMP_LIST}
- * {@link #EXTRA_FINAL_URI}
- *
- * TODO: future systemapi
- */
- public static final String ACTION_DOWNLOAD_RESULT_INTERNAL =
- "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
-
- /**
- * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
- * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
- * extras are
- * {@link #EXTRA_REQUEST}
- *
- * Optional extras are
- * {@link #EXTRA_FD_COUNT} (0 if not present)
- * {@link #EXTRA_PAUSED_LIST} (empty if not present)
- *
- * TODO: future systemapi
- */
- public static final String ACTION_FILE_DESCRIPTOR_REQUEST =
- "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
-
- /**
- * The MBMS middleware should send this when it wishes to clean up temp files in the app's
- * filesystem. Mandatory extras are:
- * {@link #EXTRA_TEMP_FILES_IN_USE}
- *
- * TODO: future systemapi
- */
- public static final String ACTION_CLEANUP =
- "android.telephony.mbms.action.CLEANUP";
/**
* Integer extra indicating the result code of the download. One of
* {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
- * TODO: Not systemapi.
*/
public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
/**
* Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
* is for. Must not be null.
- * TODO: Not systemapi.
*/
public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
/**
- * Extra containing the {@link DownloadRequest} for which the download result or file
- * descriptor request is for. Must not be null.
- * TODO: future systemapi (here and and all extras) except the three for the app intent
- */
- public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
-
- /**
- * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this
- * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp
- * files will be deleted upon receipt of the intent.
- * May be null.
- */
- public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
-
- /**
- * Extra containing a single {@link Uri} indicating the path to the temp file in which the
- * decoded downloaded file resides. Must not be null.
- */
- public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
-
- /**
- * Extra containing an integer indicating the number of temp files requested.
- */
- public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
-
- /**
- * Extra containing a list of {@link Uri}s that the middleware is requesting access to via
- * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s
- * should have scheme {@code file://}.
- */
- public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
-
- /**
- * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
- * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant
- * to be used for new file downloads.
- */
- public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
-
- /**
- * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
- * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These
- * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide
- * access to previously paused downloads.
- */
- public static final String EXTRA_PAUSED_URI_LIST =
- "android.telephony.mbms.extra.PAUSED_URI_LIST";
-
- /**
- * Extra containing a string that points to the middleware's knowledge of where the temp file
- * root for the app is. The path should be a canonical path as returned by
- * {@link File#getCanonicalPath()}
- */
- public static final String EXTRA_TEMP_FILE_ROOT =
- "android.telephony.mbms.extra.TEMP_FILE_ROOT";
-
- /**
- * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
- * still using.
- */
- public static final String EXTRA_TEMP_FILES_IN_USE =
- "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
-
- /**
- * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
- * file-descriptor requests and cleanup requests to specify which service they want to
- * request temp files or clean up temp files for, respectively.
- */
- public static final String EXTRA_SERVICE_INFO =
- "android.telephony.mbms.extra.SERVICE_INFO";
-
- /**
* Extra containing a single {@link Uri} indicating the location of the successfully
* downloaded file. Set on the intent provided via
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
* Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
* {@link #RESULT_SUCCESSFUL}.
- * TODO: Not systemapi.
*/
public static final String EXTRA_COMPLETED_FILE_URI =
"android.telephony.mbms.extra.COMPLETED_FILE_URI";
public static final int RESULT_SUCCESSFUL = 1;
- public static final int RESULT_CANCELLED = 2;
- public static final int RESULT_EXPIRED = 3;
+ public static final int RESULT_CANCELLED = 2;
+ public static final int RESULT_EXPIRED = 3;
+ public static final int RESULT_IO_ERROR = 4;
// TODO - more results!
/** @hide */
@@ -207,8 +99,16 @@
public static final int STATUS_PENDING_REPAIR = 3;
public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
private final Context mContext;
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+ }
+ };
private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
private final MbmsDownloadManagerCallback mCallback;
@@ -236,10 +136,21 @@
*
* Note that this call will bind a remote service and that may take a bit. The instance of
* {@link MbmsDownloadManager} that is returned will not be ready for use until
- * {@link IMbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
+ * {@link MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
* If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
*
- * This also may throw an {@link IllegalArgumentException} or a {@link MbmsException}.
+ * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+ *
+ * You may only have one instance of {@link MbmsDownloadManager} per UID. If you call this
+ * method while there is an active instance of {@link MbmsDownloadManager} in your process
+ * (in other words, one that has not had {@link #dispose()} called on it), this method will
+ * throw an {@link MbmsException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsDownloadManagerCallback#error(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call dispose() on the instance of
+ * {@link MbmsDownloadManager} that you received before calling this method again.
*
* @param context The instance of {@link Context} to use
* @param listener A callback to get asynchronous error messages and file service updates.
@@ -249,8 +160,16 @@
public static MbmsDownloadManager create(Context context,
MbmsDownloadManagerCallback listener, int subscriptionId)
throws MbmsException {
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+ }
MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
- mdm.bindAndInitialize();
+ try {
+ mdm.bindAndInitialize();
+ } catch (MbmsException e) {
+ sIsInitialized.set(false);
+ throw e;
+ }
return mdm;
}
@@ -266,16 +185,27 @@
result = downloadService.initialize(mSubscriptionId, mCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Service died before initialization");
+ sIsInitialized.set(false);
return;
} catch (RuntimeException e) {
Log.e(LOG_TAG, "Runtime exception during initialization");
- mCallback.error(
+ sendErrorToApp(
MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString());
+ sIsInitialized.set(false);
return;
}
if (result != MbmsException.SUCCESS) {
- mCallback.error(result, "Error returned during initialization");
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
return;
}
mService.set(downloadService);
@@ -283,6 +213,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
mService.set(null);
}
});
@@ -292,7 +223,7 @@
* An inspection API to retrieve the list of available
* {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
* The results are returned asynchronously via a call to
- * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
+ * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)}
*
* The serviceClasses argument lets the app filter on types of programming and is opaque data
* negotiated beforehand between the app and the carrier.
@@ -306,7 +237,7 @@
* {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
*
* @param classList A list of service classes which the app wishes to receive
- * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
+ * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
* about. Subsequent calls to this method will replace this list of service
* classes (i.e. the middleware will no longer send updates for services
* matching classes only in the old list).
@@ -336,14 +267,15 @@
* local instance of {@link android.content.SharedPreferences} and by the middleware.
*
* If this method is not called at least once before calling
- * {@link #download(DownloadRequest, IDownloadCallback)}, the framework
+ * {@link #download(DownloadRequest, DownloadProgressListener)}, the framework
* will default to a directory formed by the concatenation of the app's files directory and
* {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
*
* Before calling this method, the app must cancel all of its pending
* {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
* an {@link MbmsException} will be thrown with code
- * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+ * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
+ * provided directory is the same as what has been previously configured.
*
* The {@link File} supplied as a root temp file directory must already exist. If not, an
* {@link IllegalArgumentException} will be thrown.
@@ -384,6 +316,26 @@
}
/**
+ * Retrieves the currently configured temp file root directory. Returns the file that was
+ * configured via {@link #setTempFileRootDirectory(File)} or the default directory
+ * {@link #download(DownloadRequest, DownloadProgressListener)} was called without ever setting
+ * the temp file root. If neither method has been called since the last time the app's shared
+ * preferences were reset, returns null.
+ *
+ * @return A {@link File} pointing to the configured temp file directory, or null if not yet
+ * configured.
+ */
+ public @Nullable File getTempFileRootDirectory() {
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
+ if (path != null) {
+ return new File(path);
+ }
+ return null;
+ }
+
+ /**
* Requests a download of a file that is available via multicast.
*
* downloadListener is an optional callback object which can be used to get progress reports
@@ -404,7 +356,7 @@
* @param progressListener Optional listener that will be provided progress updates
* if the app is running.
*/
- public void download(DownloadRequest request, IDownloadProgressListener progressListener)
+ public void download(DownloadRequest request, DownloadProgressListener progressListener)
throws MbmsException {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
@@ -434,7 +386,7 @@
/**
* Returns a list of pending {@link DownloadRequest}s that originated from this application.
* A pending request is one that was issued via
- * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through
+ * {@link #download(DownloadRequest, DownloadProgressListener)} but not cancelled through
* {@link #cancelDownload(DownloadRequest)}.
* @return A list, possibly empty, of {@link DownloadRequest}s
*/
@@ -550,43 +502,15 @@
return;
}
downloadService.dispose(mSubscriptionId);
- mService.set(null);
} catch (RemoteException e) {
// Ignore
Log.i(LOG_TAG, "Remote exception while disposing of service");
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
}
}
- /**
- * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
- * the various intents from the middleware should be targeted towards.
- * @param uid The uid of the frontend app.
- * @return The component name of the receiver that the middleware should send its intents to,
- * or null if the app didn't declare it in the manifest.
- *
- * @hide
- * future systemapi
- */
- public static ComponentName getAppReceiverFromUid(Context context, int uid) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames == null) {
- return null;
- }
-
- for (String packageName : packageNames) {
- ComponentName candidate = new ComponentName(packageName,
- MbmsDownloadReceiver.class.getCanonicalName());
- Intent queryIntent = new Intent();
- queryIntent.setComponent(candidate);
- List<ResolveInfo> receivers =
- context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
- if (receivers != null && receivers.size() > 0) {
- return candidate;
- }
- }
- return null;
- }
-
private void writeDownloadRequestToken(DownloadRequest request) {
File token = getDownloadRequestTokenPath(request);
if (!token.getParentFile().exists()) {
@@ -651,4 +575,12 @@
}
}
}
+
+ private void sendErrorToApp(int errorCode, String message) {
+ try {
+ mCallback.error(errorCode, message);
+ } catch (RemoteException e) {
+ // Ignore, should not happen locally.
+ }
+ }
}
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index 911f83f..7a6631a 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -16,11 +16,17 @@
package android.telephony;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
+import android.telephony.mbms.InternalStreamingManagerCallback;
+import android.telephony.mbms.InternalStreamingServiceCallback;
import android.telephony.mbms.MbmsException;
import android.telephony.mbms.MbmsStreamingManagerCallback;
import android.telephony.mbms.MbmsUtils;
@@ -31,6 +37,7 @@
import android.util.Log;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -41,21 +48,42 @@
*/
public class MbmsStreamingManager {
private static final String LOG_TAG = "MbmsStreamingManager";
+
+ /**
+ * Service action which must be handled by the middleware implementing the MBMS streaming
+ * interface.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String MBMS_STREAMING_SERVICE_ACTION =
"android.telephony.action.EmbmsStreaming";
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
- private MbmsStreamingManagerCallback mCallbackToApp;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sIsInitialized.set(false);
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+ }
+ };
+
+ private InternalStreamingManagerCallback mInternalCallback;
private final Context mContext;
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
/** @hide */
private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback,
- int subscriptionId) {
+ int subscriptionId, Handler handler) {
mContext = context;
- mCallbackToApp = callback;
mSubscriptionId = subscriptionId;
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+ mInternalCallback = new InternalStreamingManagerCallback(callback, handler);
}
/**
@@ -65,27 +93,62 @@
* main thread. This may throw an {@link MbmsException}, indicating errors that may happen
* during the initialization or binding process.
*
+ *
+ * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this
+ * method while there is an active instance of {@link MbmsStreamingManager} in your process
+ * (in other words, one that has not had {@link #dispose()} called on it), this method will
+ * throw an {@link MbmsException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsStreamingManagerCallback#onError(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call dispose() on the instance of
+ * {@link MbmsStreamingManager} that you received before calling this method again.
+ *
* @param context The {@link Context} to use.
* @param callback A callback object on which you wish to receive results of asynchronous
* operations.
* @param subscriptionId The subscription ID to use.
+ * @param handler The handler you wish to receive callbacks on. If null, callbacks will be
+ * processed on the main looper (in other words, the looper returned from
+ * {@link Looper#getMainLooper()}).
*/
public static MbmsStreamingManager create(Context context,
- MbmsStreamingManagerCallback callback, int subscriptionId)
+ MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler)
throws MbmsException {
- MbmsStreamingManager manager = new MbmsStreamingManager(context, callback, subscriptionId);
- manager.bindAndInitialize();
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+ }
+ MbmsStreamingManager manager = new MbmsStreamingManager(context, callback,
+ subscriptionId, handler);
+ try {
+ manager.bindAndInitialize();
+ } catch (MbmsException e) {
+ sIsInitialized.set(false);
+ throw e;
+ }
return manager;
}
/**
* Create a new MbmsStreamingManager using the system default data subscription ID.
- * See {@link #create(Context, MbmsStreamingManagerCallback, int)}.
+ * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+ */
+ public static MbmsStreamingManager create(Context context,
+ MbmsStreamingManagerCallback callback, Handler handler)
+ throws MbmsException {
+ return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+ }
+
+ /**
+ * Create a new MbmsStreamingManager using the system default data subscription ID and
+ * default {@link Handler}.
+ * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
*/
public static MbmsStreamingManager create(Context context,
MbmsStreamingManagerCallback callback)
throws MbmsException {
- return create(context, callback, SubscriptionManager.getDefaultSubscriptionId());
+ return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
}
/**
@@ -95,17 +158,19 @@
* May throw an {@link IllegalStateException}
*/
public void dispose() {
- IMbmsStreamingService streamingService = mService.get();
- if (streamingService == null) {
- // Ignore and return, assume already disposed.
- return;
- }
try {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
+ // Ignore and return, assume already disposed.
+ return;
+ }
streamingService.dispose(mSubscriptionId);
} catch (RemoteException e) {
// Ignore for now
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
}
- mService.set(null);
}
/**
@@ -139,16 +204,17 @@
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService.set(null);
+ sIsInitialized.set(false);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
}
}
/**
- * Starts streaming a requested service, reporting status to the indicated listener.
+ * Starts streaming a requested service, reporting status to the indicated callback.
* Returns an object used to control that stream. The stream may not be ready for consumption
* immediately upon return from this method -- wait until the streaming state has been
* reported via
- * {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int, int)}
+ * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
*
* May throw an
* {@link MbmsException} containing any of the error codes in
@@ -158,34 +224,44 @@
*
* May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
- * Asynchronous errors through the listener include any of the errors in
+ * Asynchronous errors through the callback include any of the errors in
* {@link android.telephony.mbms.MbmsException.GeneralErrors} or
* {@link android.telephony.mbms.MbmsException.StreamingErrors}.
*
* @param serviceInfo The information about the service to stream.
- * @param listener A listener that'll be called when something about the stream changes.
+ * @param callback A callback that'll be called when something about the stream changes.
+ * @param handler A handler that calls to {@code callback} should be called on. If null,
+ * defaults to the handler provided via
+ * {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
* @return An instance of {@link StreamingService} through which the stream can be controlled.
*/
public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
- StreamingServiceCallback listener) throws MbmsException {
+ StreamingServiceCallback callback, Handler handler) throws MbmsException {
IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
}
+ InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
+ callback, handler == null ? mInternalCallback.getHandler() : handler);
+
+ StreamingService serviceForApp = new StreamingService(
+ mSubscriptionId, streamingService, serviceInfo, serviceCallback);
+
try {
int returnCode = streamingService.startStreaming(
- mSubscriptionId, serviceInfo.getServiceId(), listener);
+ mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService.set(null);
+ sIsInitialized.set(false);
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
}
- return new StreamingService(mSubscriptionId, streamingService, serviceInfo, listener);
+ return serviceForApp;
}
private void bindAndInitialize() throws MbmsException {
@@ -197,19 +273,34 @@
IMbmsStreamingService.Stub.asInterface(service);
int result;
try {
- result = streamingService.initialize(mCallbackToApp, mSubscriptionId);
+ result = streamingService.initialize(mInternalCallback,
+ mSubscriptionId);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Service died before initialization");
+ sendErrorToApp(
+ MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
return;
} catch (RuntimeException e) {
Log.e(LOG_TAG, "Runtime exception during initialization");
- mCallbackToApp.error(
+ sendErrorToApp(
MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString());
+ sIsInitialized.set(false);
return;
}
if (result != MbmsException.SUCCESS) {
- mCallbackToApp.error(result, "Error returned during initialization");
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
return;
}
mService.set(streamingService);
@@ -217,8 +308,17 @@
@Override
public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
mService.set(null);
}
});
}
+
+ private void sendErrorToApp(int errorCode, String message) {
+ try {
+ mInternalCallback.error(errorCode, message);
+ } catch (RemoteException e) {
+ // Ignore, should not happen locally.
+ }
+ }
}
diff --git a/telephony/java/android/telephony/mbms/DownloadProgressListener.java b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
index d6bd5dc..d91e9ad 100644
--- a/telephony/java/android/telephony/mbms/DownloadProgressListener.java
+++ b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
@@ -16,6 +16,8 @@
package android.telephony.mbms;
+import android.os.RemoteException;
+
/**
* A optional listener class used by download clients to track progress.
* @hide
@@ -38,8 +40,9 @@
* @param currentDecodedSize is the number of bytes that have been decoded.
* @param fullDecodedSize is the total number of bytes that make up the final decoded content.
*/
+ @Override
public void progress(DownloadRequest request, FileInfo fileInfo,
int currentDownloadSize, int fullDownloadSize,
- int currentDecodedSize, int fullDecodedSize) {
+ int currentDecodedSize, int fullDecodedSize) throws RemoteException {
}
}
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 01e0bbd..eae9011 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -25,6 +25,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -77,12 +78,18 @@
private String appIntent;
private int version = CURRENT_VERSION;
+ /**
+ * Sets the service from which the download request to be built will download from.
+ * @param serviceInfo
+ * @return
+ */
public Builder setServiceInfo(FileServiceInfo serviceInfo) {
fileServiceId = serviceInfo.getServiceId();
return this;
}
/**
+ * Set the service ID for the download request. For use by the middleware only.
* @hide
* TODO: systemapi
*/
@@ -91,11 +98,23 @@
return this;
}
+ /**
+ * Sets the source URI for the download request to be built.
+ * @param source
+ * @return
+ */
public Builder setSource(Uri source) {
this.source = source;
return this;
}
+ /**
+ * Sets the destination URI for the download request to be built. The middleware should
+ * not set this directly.
+ * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
+ * final destination of the download.
+ * @return
+ */
public Builder setDest(Uri dest) {
if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
throw new IllegalArgumentException("Destination uri must not exceed length " +
@@ -105,11 +124,25 @@
return this;
}
- public Builder setSubscriptionId(int sub) {
- this.subscriptionId = sub;
+ /**
+ * Set the subscription ID on which the file(s) should be downloaded.
+ * @param subscriptionId
+ * @return
+ */
+ public Builder setSubscriptionId(int subscriptionId) {
+ this.subscriptionId = subscriptionId;
return this;
}
+ /**
+ * Set the {@link Intent} that should be sent when the download completes or fails. This
+ * should be an intent with a explicit {@link android.content.ComponentName} targeted to a
+ * {@link android.content.BroadcastReceiver} in the app's package.
+ *
+ * The middleware should not use this method.
+ * @param intent
+ * @return
+ */
public Builder setAppIntent(Intent intent) {
this.appIntent = intent.toUri(0);
if (this.appIntent.length() > MAX_APP_INTENT_SIZE) {
@@ -120,7 +153,12 @@
}
/**
- * For use by middleware only
+ * For use by the middleware to set the byte array of opaque data. The opaque data
+ * includes information about the download request that is used by the client app and the
+ * manager code, but is irrelevant to the middleware.
+ * @param data A byte array, the contents of which should have been originally obtained
+ * from {@link DownloadRequest#getOpaqueData()}.
+ * @return
* TODO: systemapi
* @hide
*/
@@ -201,22 +239,40 @@
out.writeInt(version);
}
+ /**
+ * @return The ID of the file service to download from.
+ */
public String getFileServiceId() {
return fileServiceId;
}
+ /**
+ * @return The source URI to download from
+ */
public Uri getSourceUri() {
return sourceUri;
}
+ /**
+ * For use by the client app only.
+ * @return The URI of the final destination of the download.
+ */
public Uri getDestinationUri() {
return destinationUri;
}
+ /**
+ * @return The subscription ID on which to perform MBMS operations.
+ */
public int getSubscriptionId() {
return subscriptionId;
}
+ /**
+ * For internal use -- returns the intent to send to the app after download completion or
+ * failure.
+ * @hide
+ */
public Intent getIntentForApp() {
try {
return Intent.parseUri(serializedResultIntentForApp, 0);
@@ -226,6 +282,10 @@
}
/**
+ * For use by the middleware only. The byte array returned from this method should be
+ * persisted and sent back to the app upon download completion or failure by passing it into
+ * {@link Builder#setOpaqueData(byte[])}.
+ * @return A byte array of opaque data to persist.
* @hide
* TODO: systemapi
*/
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index 1b87393..f97131d 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -38,16 +38,6 @@
*/
private final String mimeType;
- /**
- * The size of the file in bytes.
- */
- private final long size;
-
- /**
- * The MD5 hash of the file.
- */
- private final byte md5Hash[];
-
public static final Parcelable.Creator<FileInfo> CREATOR =
new Parcelable.Creator<FileInfo>() {
@Override
@@ -61,29 +51,24 @@
}
};
- public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) {
+ /**
+ * @hide
+ * TODO: systemapi
+ */
+ public FileInfo(Uri uri, String mimeType) {
this.uri = uri;
this.mimeType = mimeType;
- this.size = size;
- this.md5Hash = md5Hash;
}
private FileInfo(Parcel in) {
uri = in.readParcelable(null);
mimeType = in.readString();
- size = in.readLong();
- int arraySize = in.readInt();
- md5Hash = new byte[arraySize];
- in.readByteArray(md5Hash);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(uri, flags);
dest.writeString(mimeType);
- dest.writeLong(size);
- dest.writeInt(md5Hash.length);
- dest.writeByteArray(md5Hash);
}
@Override
@@ -98,12 +83,4 @@
public String getMimeType() {
return mimeType;
}
-
- public long getSize() {
- return size;
- }
-
- public byte[] getMd5Hash() {
- return md5Hash;
- }
}
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 6646dc8..8afe4d3 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -32,6 +32,7 @@
public class FileServiceInfo extends ServiceInfo implements Parcelable {
private final List<FileInfo> files;
+ /** @hide TODO: systemapi */
public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
List<Locale> newLocales, String newServiceId, Date start, Date end,
List<FileInfo> newFiles) {
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java
new file mode 100644
index 0000000..b52df8c
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+
+import java.util.List;
+
+/** @hide */
+public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
+ private final Handler mHandler;
+ private final MbmsStreamingManagerCallback mAppCallback;
+
+ public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback,
+ Handler handler) {
+ mAppCallback = appCallback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onError(errorCode, message);
+ }
+ });
+ }
+
+ @Override
+ public void streamingServicesUpdated(List<StreamingServiceInfo> services)
+ throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStreamingServicesUpdated(services);
+ }
+ });
+ }
+
+ @Override
+ public void middlewareReady() throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onMiddlewareReady();
+ }
+ });
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
new file mode 100644
index 0000000..bb337b2
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+/** @hide */
+public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
+ private final StreamingServiceCallback mAppCallback;
+ private final Handler mHandler;
+
+ public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+ mAppCallback = appCallback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onError(errorCode, message);
+ }
+ });
+ }
+
+ @Override
+ public void streamStateUpdated(int state, int reason) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStreamStateUpdated(state, reason);
+ }
+ });
+ }
+
+ @Override
+ public void mediaDescriptionUpdated() throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onMediaDescriptionUpdated();
+ }
+ });
+ }
+
+ @Override
+ public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+ }
+ });
+ }
+
+ @Override
+ public void streamMethodUpdated(int methodType) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStreamMethodUpdated(methodType);
+ }
+ });
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
index ba25f66..17291d0 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -16,6 +16,9 @@
package android.telephony.mbms;
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadManager;
+
import java.util.List;
/**
@@ -24,12 +27,8 @@
*/
public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
- public final static int ERROR_CARRIER_NOT_SUPPORTED = 1;
- public final static int ERROR_UNABLE_TO_INITIALIZE = 2;
- public final static int ERROR_UNABLE_TO_ALLOCATE_MEMORY = 3;
-
-
- public void error(int errorCode, String message) {
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
// default implementation empty
}
@@ -45,7 +44,8 @@
* @param services a List of FileServiceInfos
*
*/
- public void fileServicesUpdated(List<FileServiceInfo> services) {
+ @Override
+ public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
// default implementation empty
}
@@ -58,7 +58,7 @@
* or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
*/
@Override
- public void middlewareReady() {
+ public void middlewareReady() throws RemoteException {
// default implementation empty
}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 339ff39..ba7d120 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -25,6 +25,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.telephony.MbmsDownloadManager;
+import android.telephony.mbms.vendor.VendorIntents;
import android.util.Log;
import java.io.File;
@@ -56,9 +57,9 @@
/**
* Indicates that the intent sent had an invalid action. This will be the result if
* {@link Intent#getAction()} returns anything other than
- * {@link MbmsDownloadManager#ACTION_DOWNLOAD_RESULT_INTERNAL},
- * {@link MbmsDownloadManager#ACTION_FILE_DESCRIPTOR_REQUEST}, or
- * {@link MbmsDownloadManager#ACTION_CLEANUP}.
+ * {@link VendorIntents#ACTION_DOWNLOAD_RESULT_INTERNAL},
+ * {@link VendorIntents#ACTION_FILE_DESCRIPTOR_REQUEST}, or
+ * {@link VendorIntents#ACTION_CLEANUP}.
* This is a fatal result code and no result extras should be expected.
*/
public static final int RESULT_INVALID_ACTION = 1;
@@ -70,7 +71,7 @@
public static final int RESULT_MALFORMED_INTENT = 2;
/**
- * Indicates that the supplied value for {@link MbmsDownloadManager#EXTRA_TEMP_FILE_ROOT}
+ * Indicates that the supplied value for {@link VendorIntents#EXTRA_TEMP_FILE_ROOT}
* does not match what the app has stored.
* This is a fatal result code and no result extras should be expected.
*/
@@ -104,18 +105,18 @@
setResultCode(RESULT_MALFORMED_INTENT);
return;
}
- if (!Objects.equals(intent.getStringExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT),
+ if (!Objects.equals(intent.getStringExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT),
MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
setResultCode(RESULT_BAD_TEMP_FILE_ROOT);
return;
}
- if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+ if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
moveDownloadedFile(context, intent);
cleanupPostMove(context, intent);
- } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+ } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
generateTempFiles(context, intent);
- } else if (MbmsDownloadManager.ACTION_CLEANUP.equals(intent.getAction())) {
+ } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
cleanupTempFiles(context, intent);
} else {
setResultCode(RESULT_INVALID_ACTION);
@@ -123,16 +124,16 @@
}
private boolean verifyIntentContents(Context context, Intent intent) {
- if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+ if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) {
Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_REQUEST)) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_REQUEST)) {
Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false;
}
@@ -141,12 +142,12 @@
"Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FINAL_URI)) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_FINAL_URI)) {
Log.w(LOG_TAG, "Download result did not include the path to the final " +
"temp file. Ignoring.");
return false;
}
- DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
+ DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
File expectedTokenFile = new File(
MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
@@ -156,27 +157,27 @@
"Expected " + expectedTokenFile);
return false;
}
- } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO)) {
+ } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
Log.w(LOG_TAG, "Temp file request did not include the associated service info." +
" Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false;
}
- } else if (MbmsDownloadManager.ACTION_CLEANUP.equals(intent.getAction())) {
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO)) {
+ } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
Log.w(LOG_TAG, "Cleanup request did not include the associated service info." +
" Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE)) {
+ if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE)) {
Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " +
"Ignoring.");
return false;
@@ -186,7 +187,7 @@
}
private void moveDownloadedFile(Context context, Intent intent) {
- DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
+ DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
Intent intentForApp = request.getIntentForApp();
int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT,
@@ -200,7 +201,7 @@
}
Uri destinationUri = request.getDestinationUri();
- Uri finalTempFile = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FINAL_URI);
+ Uri finalTempFile = intent.getParcelableExtra(VendorIntents.EXTRA_FINAL_URI);
if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -225,13 +226,13 @@
}
private void cleanupPostMove(Context context, Intent intent) {
- DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
+ DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
if (request == null) {
Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
return;
}
- List<Uri> tempFiles = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_TEMP_LIST);
+ List<Uri> tempFiles = intent.getParcelableExtra(VendorIntents.EXTRA_TEMP_LIST);
if (tempFiles == null) {
return;
}
@@ -246,15 +247,15 @@
private void generateTempFiles(Context context, Intent intent) {
FileServiceInfo serviceInfo =
- intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO);
+ intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
if (serviceInfo == null) {
Log.w(LOG_TAG, "Temp file request did not include the associated service info. " +
"Ignoring.");
setResultCode(RESULT_MALFORMED_INTENT);
return;
}
- int fdCount = intent.getIntExtra(MbmsDownloadManager.EXTRA_FD_COUNT, 0);
- List<Uri> pausedList = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_PAUSED_LIST);
+ int fdCount = intent.getIntExtra(VendorIntents.EXTRA_FD_COUNT, 0);
+ List<Uri> pausedList = intent.getParcelableExtra(VendorIntents.EXTRA_PAUSED_LIST);
if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
Log.i(LOG_TAG, "No temp files actually requested. Ending.");
@@ -269,8 +270,8 @@
generateUrisForPausedFiles(context, serviceInfo, pausedList);
Bundle result = new Bundle();
- result.putParcelableArrayList(MbmsDownloadManager.EXTRA_FREE_URI_LIST, freshTempFiles);
- result.putParcelableArrayList(MbmsDownloadManager.EXTRA_PAUSED_URI_LIST, pausedFiles);
+ result.putParcelableArrayList(VendorIntents.EXTRA_FREE_URI_LIST, freshTempFiles);
+ result.putParcelableArrayList(VendorIntents.EXTRA_PAUSED_URI_LIST, pausedFiles);
setResultCode(RESULT_OK);
setResultExtras(result);
}
@@ -353,11 +354,11 @@
private void cleanupTempFiles(Context context, Intent intent) {
FileServiceInfo serviceInfo =
- intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO);
+ intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
serviceInfo.getServiceId());
final List<Uri> filesInUse =
- intent.getParcelableArrayListExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE);
+ intent.getParcelableArrayListExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE);
File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 8888119..7cf8792 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -31,7 +31,7 @@
/**
* Indicates that the app attempted to perform an operation on an instance of
- * {@link android.telephony.MbmsDownloadManager} or
+ * TODO: link android.telephony.MbmsDownloadManager or
* {@link android.telephony.MbmsStreamingManager} without being bound to the middleware.
*/
public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
@@ -44,10 +44,11 @@
* middleware. They are applicable to both streaming and file-download use-cases.
*/
public static class InitializationErrors {
+ private InitializationErrors() {}
/**
* Indicates that the app tried to create more than one instance each of
* {@link android.telephony.MbmsStreamingManager} or
- * {@link android.telephony.MbmsDownloadManager}.
+ * TODO: link android.telephony.MbmsDownloadManager
*/
public static final int ERROR_DUPLICATE_INITIALIZE = 101;
/** Indicates that the app is not authorized to access media via MBMS.*/
@@ -61,10 +62,11 @@
* streaming and file-download.
*/
public static class GeneralErrors {
+ private GeneralErrors() {}
/**
* Indicates that the app attempted to perform an operation before receiving notification
- * that the middleware is ready via {@link MbmsStreamingManagerCallback#middlewareReady()}
- * or {@link MbmsDownloadManagerCallback#middlewareReady()}.
+ * that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()}
+ * or TODO: link MbmsDownloadManagerCallback#middlewareReady
*/
public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
/**
@@ -97,6 +99,7 @@
* Indicates the errors that are applicable only to the streaming use-case
*/
public static class StreamingErrors {
+ private StreamingErrors() {}
/** Indicates that the middleware cannot start a stream due to too many ongoing streams */
public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301;
@@ -105,7 +108,8 @@
/**
* Indicates that the app called
- * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)}
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(
+ * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
* more than once for the same {@link StreamingServiceInfo}.
*/
public static final int ERROR_DUPLICATE_START_STREAM = 303;
@@ -113,6 +117,8 @@
/**
* Indicates the errors that are applicable only to the file-download use-case
+ * TODO: unhide
+ * @hide
*/
public static class DownloadErrors {
/**
@@ -127,9 +133,7 @@
private final int mErrorCode;
- /** @hide
- * TODO: future systemapi
- */
+ /** @hide */
public MbmsException(int errorCode) {
super();
mErrorCode = errorCode;
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
index 2e91be9..831050e 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
@@ -16,20 +16,26 @@
package android.telephony.mbms;
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.MbmsStreamingManager;
+
import java.util.List;
/**
- * A Parcelable class with Cell-Broadcast service information.
+ * A callback class that is used to receive information from the middleware on MBMS streaming
+ * services. An instance of this object should be passed into
+ * {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}.
* @hide
*/
-public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
-
- public final static int ERROR_CARRIER_NOT_SUPPORTED = 1;
- public final static int ERROR_UNABLE_TO_INITIALIZE = 2;
- public final static int ERROR_UNABLE_TO_ALLOCATE_MEMORY = 3;
-
-
- public void error(int errorCode, String message) {
+public class MbmsStreamingManagerCallback {
+ /**
+ * Called by the middleware when it has detected an error condition. The possible error codes
+ * are listed in {@link MbmsException}.
+ * @param errorCode The error code.
+ * @param message A human-readable message generated by the middleware for debugging purposes.
+ */
+ public void onError(int errorCode, String message) {
// default implementation empty
}
@@ -45,7 +51,7 @@
* @param services a List of StreamingServiceInfos
*
*/
- public void streamingServicesUpdated(List<StreamingServiceInfo> services) {
+ public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
// default implementation empty
}
@@ -57,8 +63,7 @@
* being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
* or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
*/
- @Override
- public void middlewareReady() {
+ public void onMiddlewareReady() {
// default implementation empty
}
}
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index f9ad44c..423ae01 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -30,43 +30,22 @@
import java.util.Set;
/**
- * A Parcelable class with Cell-Broadcast service information.
+ * Describes a cell-broadcast service. This class should not be instantiated directly -- use
+ * {@link StreamingServiceInfo} or FileServiceInfo TODO: add link once that's unhidden
* @hide
*/
-public class ServiceInfo implements Parcelable {
+public class ServiceInfo {
// arbitrary limit on the number of locale -> name pairs we support
final static int MAP_LIMIT = 1000;
- /**
- * User displayable names listed by language. Unmodifiable.
- */
- final Map<Locale, String> names;
- /**
- * The class name for this service - used to catagorize and filter
- */
- final String className;
+ private final Map<Locale, String> names;
+ private final String className;
+ private final List<Locale> locales;
+ private final String serviceId;
+ private final Date sessionStartTime;
+ private final Date sessionEndTime;
- /**
- * The languages available for this service content
- */
- final List<Locale> locales;
-
- /**
- * The carrier's identifier for the service.
- */
- final String serviceId;
-
- /**
- * The start time indicating when this service will be available.
- */
- final Date sessionStartTime;
-
- /**
- * The end time indicating when this sesion stops being available.
- */
- final Date sessionEndTime;
-
-
+ /** @hide */
public ServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales,
String newServiceId, Date start, Date end) {
if (newNames == null || newNames.isEmpty() || TextUtils.isEmpty(newClassName)
@@ -89,20 +68,8 @@
sessionEndTime = (Date)end.clone();
}
- public static final Parcelable.Creator<FileServiceInfo> CREATOR =
- new Parcelable.Creator<FileServiceInfo>() {
- @Override
- public FileServiceInfo createFromParcel(Parcel source) {
- return new FileServiceInfo(source);
- }
-
- @Override
- public FileServiceInfo[] newArray(int size) {
- return new FileServiceInfo[size];
- }
- };
-
- ServiceInfo(Parcel in) {
+ /** @hide */
+ protected ServiceInfo(Parcel in) {
int mapCount = in.readInt();
if (mapCount > MAP_LIMIT || mapCount < 0) {
throw new RuntimeException("bad map length" + mapCount);
@@ -128,7 +95,7 @@
sessionEndTime = (java.util.Date) in.readSerializable();
}
- @Override
+ /** @hide */
public void writeToParcel(Parcel dest, int flags) {
Set<Locale> keySet = names.keySet();
dest.writeInt(keySet.size());
@@ -147,31 +114,44 @@
dest.writeSerializable(sessionEndTime);
}
- @Override
- public int describeContents() {
- return 0;
- }
-
+ /**
+ * User displayable names listed by language. Do not modify the map returned from this method.
+ */
public Map<Locale, String> getNames() {
return names;
}
+ /**
+ * The class name for this service - used to categorize and filter
+ */
public String getClassName() {
return className;
}
+ /**
+ * The languages available for this service content
+ */
public List<Locale> getLocales() {
return locales;
}
+ /**
+ * The carrier's identifier for the service.
+ */
public String getServiceId() {
return serviceId;
}
+ /**
+ * The start time indicating when this service will be available.
+ */
public Date getSessionStartTime() {
return sessionStartTime;
}
+ /**
+ * The end time indicating when this session stops being available.
+ */
public Date getSessionEndTime() {
return sessionEndTime;
}
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index c49f8a9..5c4b786 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -26,13 +26,17 @@
import java.lang.annotation.RetentionPolicy;
/**
+ * Class used to represent a single MBMS stream. After a stream has been started with
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * StreamingServiceCallback, android.os.Handler)},
+ * this class is used to hold information about the stream and control it.
* @hide
*/
public class StreamingService {
private static final String LOG_TAG = "MbmsStreamingService";
/**
- * The state of a stream, reported via {@link StreamingServiceCallback#streamStateUpdated}
+ * The state of a stream, reported via {@link StreamingServiceCallback#onStreamStateUpdated}
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@@ -44,7 +48,7 @@
/**
* The reason for a stream state change, reported via
- * {@link StreamingServiceCallback#streamStateUpdated}
+ * {@link StreamingServiceCallback#onStreamStateUpdated}
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@@ -60,7 +64,8 @@
/**
* State changed due to a call to {@link #stopStreaming()} or
- * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)}
+ * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * StreamingServiceCallback, android.os.Handler)}
*/
public static final int REASON_BY_USER_REQUEST = 1;
@@ -87,27 +92,28 @@
/**
* State changed due to the device leaving the where this stream is being broadcast.
*/
- public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 5;
+ public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
/**
* The method of transmission currently used for a stream,
- * reported via {@link StreamingServiceCallback#streamMethodUpdated}
+ * reported via {@link StreamingServiceCallback#onStreamMethodUpdated}
*/
public final static int BROADCAST_METHOD = 1;
public final static int UNICAST_METHOD = 2;
private final int mSubscriptionId;
private final StreamingServiceInfo mServiceInfo;
- private final IStreamingServiceCallback mCallback;
+ private final InternalStreamingServiceCallback mCallback;
private IMbmsStreamingService mService;
+
/**
* @hide
*/
public StreamingService(int subscriptionId,
IMbmsStreamingService service,
StreamingServiceInfo streamingServiceInfo,
- IStreamingServiceCallback callback) {
+ InternalStreamingServiceCallback callback) {
mSubscriptionId = subscriptionId;
mService = service;
mServiceInfo = streamingServiceInfo;
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index cab9c23..eeef8bc 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -17,10 +17,11 @@
package android.telephony.mbms;
/**
- * A Callback class for use when the application is actively streaming content.
+ * A callback class for use when the application is actively streaming content. The middleware
+ * will provide updates on the status of the stream via this callback.
* @hide
*/
-public class StreamingServiceCallback extends IStreamingServiceCallback.Stub {
+public class StreamingServiceCallback {
/**
* Indicates broadcast signal strength is not available for this service.
@@ -31,8 +32,13 @@
*/
public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
- @Override
- public void error(int errorCode, String message) {
+ /**
+ * Called by the middleware when it has detected an error condition in this stream. The
+ * possible error codes are listed in {@link MbmsException}.
+ * @param errorCode The error code.
+ * @param message A human-readable message generated by the middleware for debugging purposes.
+ */
+ public void onError(int errorCode, String message) {
// default implementation empty
}
@@ -42,8 +48,7 @@
* See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
* and {@link StreamingService#STATE_STALLED}.
*/
- @Override
- public void streamStateUpdated(@StreamingService.StreamingState int state,
+ public void onStreamStateUpdated(@StreamingService.StreamingState int state,
@StreamingService.StreamingStateChangeReason int reason) {
// default implementation empty
}
@@ -58,8 +63,7 @@
* This may be called when a looping stream hits the end or
* when parameters have changed to account for time drift.
*/
- @Override
- public void mediaDescriptionUpdated() {
+ public void onMediaDescriptionUpdated() {
// default implementation empty
}
@@ -73,8 +77,7 @@
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this service due to timing, geography or popularity.
*/
- @Override
- public void broadcastSignalStrengthUpdated(int signalStrength) {
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
// default implementation empty
}
@@ -94,8 +97,7 @@
* See {@link StreamingService#BROADCAST_METHOD} and
* {@link StreamingService#UNICAST_METHOD}
*/
- @Override
- public void streamMethodUpdated(int methodType) {
+ public void onStreamMethodUpdated(int methodType) {
// default implementation empty
}
}
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
index 77ce3bb..8e7917a 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java
@@ -16,6 +16,7 @@
package android.telephony.mbms;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,15 +26,25 @@
import java.util.Map;
/**
- * A Parcelable class Cell-Broadcast media stream information.
- * This may not have any more info than ServiceInfo, but kept for completeness.
+ * Describes a single MBMS streaming service.
* @hide
*/
-public class StreamingServiceInfo extends ServiceInfo implements Parcelable {
+public final class StreamingServiceInfo extends ServiceInfo implements Parcelable {
- public StreamingServiceInfo(Map<Locale, String> newNames, String newClassName,
- List<Locale> newLocales, String newServiceId, Date start, Date end) {
- super(newNames, newClassName, newLocales, newServiceId, start, end);
+ /**
+ * @param names User displayable names listed by language.
+ * @param className The class name for this service - used by frontend apps to categorize and
+ * filter.
+ * @param locales The languages available for this service content.
+ * @param serviceId The carrier's identifier for the service.
+ * @param start The start time indicating when this service will be available.
+ * @param end The end time indicating when this session stops being available.
+ * @hide
+ */
+ @SystemApi
+ public StreamingServiceInfo(Map<Locale, String> names, String className,
+ List<Locale> locales, String serviceId, Date start, Date end) {
+ super(names, className, locales, serviceId, start, end);
}
public static final Parcelable.Creator<StreamingServiceInfo> CREATOR =
@@ -49,7 +60,7 @@
}
};
- StreamingServiceInfo(Parcel in) {
+ private StreamingServiceInfo(Parcel in) {
super(in);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index edd5858..71713d0 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -18,10 +18,13 @@
import android.annotation.NonNull;
import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.FileServiceInfo;
import android.telephony.mbms.IDownloadProgressListener;
import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadManagerCallback;
import android.telephony.mbms.MbmsException;
import java.util.List;
@@ -44,16 +47,40 @@
* or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
* {@link IMbmsDownloadManagerCallback#error(int, String)}.
*
- * @param listener The callback to use to communicate with the app.
+ * @param callback The callback to use to communicate with the app.
* @param subscriptionId The subscription ID to use.
*/
- @Override
- public int initialize(int subscriptionId,
- IMbmsDownloadManagerCallback listener) throws RemoteException {
+ public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback)
+ throws RemoteException {
return 0;
}
/**
+ * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * @hide
+ */
+ @Override
+ public final int initialize(int subscriptionId,
+ final IMbmsDownloadManagerCallback callback) throws RemoteException {
+ return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
+ @Override
+ public void error(int errorCode, String message) throws RemoteException {
+ callback.error(errorCode, message);
+ }
+
+ @Override
+ public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
+ callback.fileServicesUpdated(services);
+ }
+
+ @Override
+ public void middlewareReady() throws RemoteException {
+ callback.middlewareReady();
+ }
+ });
+ }
+
+ /**
* Registers serviceClasses of interest with the appName/subId key.
* Starts async fetching data on streaming services of matching classes to be reported
* later via {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
@@ -107,19 +134,36 @@
* @param downloadRequest An object describing the set of files to be downloaded.
* @param listener A listener through which the middleware can provide progress updates to
* the app while both are still running.
- * @return TODO: enumerate possible return values
+ * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors}
+ * or {@link MbmsException#SUCCESS}
+ */
+ public int download(DownloadRequest downloadRequest, DownloadProgressListener listener) {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * @hide
*/
@Override
- public int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
+ public final int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
throws RemoteException {
- return 0;
+ return download(downloadRequest, new DownloadProgressListener() {
+ @Override
+ public void progress(DownloadRequest request, FileInfo fileInfo, int
+ currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
+ fullDecodedSize) throws RemoteException {
+ listener.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
+ currentDecodedSize, fullDecodedSize);
+ }
+ });
}
/**
* Returns a list of pending {@link DownloadRequest}s that originated from the calling
* application, identified by its uid. A pending request is one that was issued via
- * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through
+ * {@link #download(DownloadRequest, IDownloadProgressListener)} but not cancelled through
* {@link #cancelDownload(DownloadRequest)}.
* The middleware must return a non-null result synchronously or throw an exception
* inheriting from {@link RuntimeException}.
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 585d5b9..8f2786f 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -17,23 +17,29 @@
package android.telephony.mbms.vendor;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.Uri;
+import android.os.Binder;
import android.os.RemoteException;
import android.telephony.mbms.IMbmsStreamingManagerCallback;
import android.telephony.mbms.IStreamingServiceCallback;
import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
import java.util.List;
/**
* @hide
- * TODO: future systemapi
*/
+//@SystemApi
public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Initialize streaming service for this app and subId, registering the listener.
*
- * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
+ * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
* will be intercepted and passed to the app as
* {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
*
@@ -41,16 +47,61 @@
* or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
* {@link IMbmsStreamingManagerCallback#error(int, String)}.
*
- * @param listener The callback to use to communicate with the app.
+ * @param callback The callback to use to communicate with the app.
* @param subscriptionId The subscription ID to use.
*/
- @Override
- public int initialize(IMbmsStreamingManagerCallback listener, int subscriptionId)
+ public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId)
throws RemoteException {
return 0;
}
/**
+ * Actual AIDL implementation that hides the callback AIDL from the middleware.
+ * @hide
+ */
+ @Override
+ public final int initialize(final IMbmsStreamingManagerCallback callback,
+ final int subscriptionId) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+
+ return initialize(new MbmsStreamingManagerCallback() {
+ @Override
+ public void onError(int errorCode, String message) {
+ try {
+ callback.error(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+ try {
+ callback.streamingServicesUpdated(services);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onMiddlewareReady() {
+ try {
+ callback.middlewareReady();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ }, subscriptionId);
+ }
+
+
+ /**
* Registers serviceClasses of interest with the appName/subId key.
* Starts async fetching data on streaming services of matching classes to be reported
* later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
@@ -82,13 +133,77 @@
*
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app has requested.
- * @param listener The listener object on which the app wishes to receive updates.
+ * @param callback The callback object on which the app wishes to receive updates.
* @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors}
*/
+ public int startStreaming(int subscriptionId, String serviceId,
+ StreamingServiceCallback callback) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation of startStreaming that hides the callback AIDL from the
+ * middleware.
+ * @hide
+ */
@Override
public int startStreaming(int subscriptionId, String serviceId,
- IStreamingServiceCallback listener) throws RemoteException {
- return 0;
+ IStreamingServiceCallback callback) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+
+ return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
+ @Override
+ public void onError(int errorCode, String message) {
+ try {
+ callback.error(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onStreamStateUpdated(@StreamingService.StreamingState int state,
+ @StreamingService.StreamingStateChangeReason int reason) {
+ try {
+ callback.streamStateUpdated(state, reason);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onMediaDescriptionUpdated() {
+ try {
+ callback.mediaDescriptionUpdated();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ try {
+ callback.broadcastSignalStrengthUpdated(signalStrength);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onStreamMethodUpdated(int methodType) {
+ try {
+ callback.streamMethodUpdated(methodType);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ });
}
/**
@@ -153,4 +268,12 @@
@Override
public void dispose(int subscriptionId) throws RemoteException {
}
+
+ /**
+ * Indicates that the app identified by the given UID and subscription ID has died.
+ * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+ * @param subscriptionId The subscription ID the app is using.
+ */
+ public void onAppCallbackDied(int uid, int subscriptionId) {
+ }
}
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorIntents.java b/telephony/java/android/telephony/mbms/vendor/VendorIntents.java
new file mode 100644
index 0000000..367c995
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/VendorIntents.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms.vendor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.MbmsDownloadReceiver;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @hide
+ * TODO: future systemapi
+ */
+public class VendorIntents {
+
+ /**
+ * The MBMS middleware should send this when a download of single file has completed or
+ * failed. Mandatory extras are
+ * {@link android.telephony.MbmsDownloadManager#EXTRA_RESULT}
+ * {@link android.telephony.MbmsDownloadManager#EXTRA_FILE_INFO}
+ * {@link #EXTRA_REQUEST}
+ * {@link #EXTRA_TEMP_LIST}
+ * {@link #EXTRA_FINAL_URI}
+ */
+ public static final String ACTION_DOWNLOAD_RESULT_INTERNAL =
+ "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+
+ /**
+ * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
+ * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
+ * extras are
+ * {@link #EXTRA_REQUEST}
+ *
+ * Optional extras are
+ * {@link #EXTRA_FD_COUNT} (0 if not present)
+ * {@link #EXTRA_PAUSED_LIST} (empty if not present)
+ */
+ public static final String ACTION_FILE_DESCRIPTOR_REQUEST =
+ "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+
+ /**
+ * The MBMS middleware should send this when it wishes to clean up temp files in the app's
+ * filesystem. Mandatory extras are:
+ * {@link #EXTRA_TEMP_FILES_IN_USE}
+ */
+ public static final String ACTION_CLEANUP =
+ "android.telephony.mbms.action.CLEANUP";
+
+ /**
+ * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this
+ * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp
+ * files will be deleted upon receipt of the intent.
+ * May be null.
+ */
+ public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+
+ /**
+ * Extra containing an integer indicating the number of temp files requested.
+ */
+ public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+
+ /**
+ * Extra containing a list of {@link Uri}s that the middleware is requesting access to via
+ * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s
+ * should have scheme {@code file://}.
+ */
+ public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+
+ /**
+ * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+ * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant
+ * to be used for new file downloads.
+ */
+ public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+
+ /**
+ * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+ * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These
+ * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide
+ * access to previously paused downloads.
+ */
+ public static final String EXTRA_PAUSED_URI_LIST =
+ "android.telephony.mbms.extra.PAUSED_URI_LIST";
+
+ /**
+ * Extra containing a string that points to the middleware's knowledge of where the temp file
+ * root for the app is. The path should be a canonical path as returned by
+ * {@link File#getCanonicalPath()}
+ */
+ public static final String EXTRA_TEMP_FILE_ROOT =
+ "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+
+ /**
+ * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
+ * still using.
+ */
+ public static final String EXTRA_TEMP_FILES_IN_USE =
+ "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+
+ /**
+ * Extra containing the {@link DownloadRequest} for which the download result or file
+ * descriptor request is for. Must not be null.
+ */
+ public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
+
+ /**
+ * Extra containing a single {@link Uri} indicating the path to the temp file in which the
+ * decoded downloaded file resides. Must not be null.
+ */
+ public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+
+ /**
+ * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
+ * file-descriptor requests and cleanup requests to specify which service they want to
+ * request temp files or clean up temp files for, respectively.
+ */
+ public static final String EXTRA_SERVICE_INFO =
+ "android.telephony.mbms.extra.SERVICE_INFO";
+
+ /**
+ * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
+ * the various intents from the middleware should be targeted towards.
+ * @param uid The uid of the frontend app.
+ * @return The component name of the receiver that the middleware should send its intents to,
+ * or null if the app didn't declare it in the manifest.
+ */
+ public static ComponentName getAppReceiverFromUid(Context context, int uid) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null) {
+ return null;
+ }
+
+ for (String packageName : packageNames) {
+ ComponentName candidate = new ComponentName(packageName,
+ MbmsDownloadReceiver.class.getCanonicalName());
+ Intent queryIntent = new Intent();
+ queryIntent.setComponent(candidate);
+ List<ResolveInfo> receivers =
+ context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+ if (receivers != null && receivers.size() > 0) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+}