Merge "Derive library path for upgraded system apps."
diff --git a/Android.mk b/Android.mk
index 6859678..a7eb03d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -406,6 +406,8 @@
# List of packages to exclude along with their descendants.
# Overrides inclusion.
LOCAL_JAR_EXCLUDE_PACKAGES := \
+ android.filterfw \
+ android.filterpacks \
android.hardware
# List of classes and interfaces which should be loaded by the Zygote.
@@ -426,6 +428,8 @@
# List of packages to include along with their descendants.
LOCAL_JAR_PACKAGES := \
+ android.filterfw \
+ android.filterpacks \
android.hardware \
com \
javax
@@ -614,7 +618,6 @@
ext \
framework \
framework2 \
- mms-common \
telephony-common \
voip-common
diff --git a/api/current.txt b/api/current.txt
index 92dce76..319f4b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -134,7 +134,6 @@
field public static final java.lang.String SET_WALLPAPER = "android.permission.SET_WALLPAPER";
field public static final java.lang.String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
field public static final java.lang.String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
- field public static final java.lang.String SIM_COMMUNICATION = "android.permission.SIM_COMMUNICATION";
field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
field public static final java.lang.String SUBSCRIBED_FEEDS_READ = "android.permission.SUBSCRIBED_FEEDS_READ";
field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE";
@@ -3334,6 +3333,7 @@
method public void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
+ method public void onBackgroundMediaPlayingChanged(boolean);
method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
method public void onConfigurationChanged(android.content.res.Configuration);
method public void onContentChanged();
@@ -3389,6 +3389,7 @@
method public boolean onSearchRequested();
method protected void onStart();
method protected void onStop();
+ method public void onStopMediaPlaying();
method protected void onTitleChanged(java.lang.CharSequence, int);
method public boolean onTouchEvent(android.view.MotionEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
@@ -3423,6 +3424,7 @@
method public void setFinishOnTouchOutside(boolean);
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
+ method public boolean setMediaPlaying(boolean);
method public final void setProgress(int);
method public final void setProgressBarIndeterminate(boolean);
method public final void setProgressBarIndeterminateVisibility(boolean);
@@ -16098,6 +16100,7 @@
public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
ctor public TvInputService.Session();
+ method public void dispatchChannelRetuned(android.net.Uri);
method public android.view.View onCreateOverlayView();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -16135,6 +16138,7 @@
public static abstract class TvView.TvInputListener {
ctor public TvView.TvInputListener();
+ method public void onChannelRetuned(java.lang.String, android.net.Uri);
method public void onError(java.lang.String, int);
}
@@ -16315,6 +16319,7 @@
field public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
field public static final int TYPE_MOBILE_MMS = 2; // 0x2
field public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+ field public static final int TYPE_VPN = 17; // 0x11
field public static final int TYPE_WIFI = 1; // 0x1
field public static final int TYPE_WIMAX = 6; // 0x6
}
@@ -27671,14 +27676,14 @@
}
public final class ConnectionRequest implements android.os.Parcelable {
- ctor public ConnectionRequest(android.net.Uri, android.os.Bundle);
- ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle);
- ctor public ConnectionRequest(android.telecomm.PhoneAccount, java.lang.String, android.net.Uri, android.os.Bundle);
+ ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle, int);
+ ctor public ConnectionRequest(android.telecomm.PhoneAccount, java.lang.String, android.net.Uri, android.os.Bundle, int);
method public int describeContents();
method public android.telecomm.PhoneAccount getAccount();
method public java.lang.String getCallId();
method public android.os.Bundle getExtras();
method public android.net.Uri getHandle();
+ method public int getVideoState();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -27851,7 +27856,7 @@
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
- field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO = "android.intent.extra.START_CALL_WITH_VIDEO";
+ field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
}
public class VideoCallProfile implements android.os.Parcelable {
@@ -30379,6 +30384,40 @@
method public void writeToParcel(android.os.Parcel, int);
}
+ public class TtsSpan implements android.text.ParcelableSpan {
+ ctor public TtsSpan(java.lang.String, android.os.PersistableBundle);
+ ctor public TtsSpan(android.os.Parcel);
+ method public int describeContents();
+ method public android.os.PersistableBundle getArgs();
+ method public int getSpanTypeId();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ANIMACY_ANIMATE = "android.animate";
+ field public static final java.lang.String ANIMACY_INANIMATE = "android.inanimate";
+ field public static final java.lang.String ARG_ANIMACY = "android.arg.animacy";
+ field public static final java.lang.String ARG_CASE = "android.arg.case";
+ field public static final java.lang.String ARG_GENDER = "android.arg.gender";
+ field public static final java.lang.String ARG_MULTIPLICITY = "android.arg.multiplicity";
+ field public static final java.lang.String ARG_NUMBER = "android.arg.number";
+ field public static final java.lang.String ARG_TEXT = "android.arg.text";
+ field public static final java.lang.String CASE_ABLATIVE = "android.ablative";
+ field public static final java.lang.String CASE_ACCUSATIVE = "android.accusative";
+ field public static final java.lang.String CASE_DATIVE = "android.dative";
+ field public static final java.lang.String CASE_GENITIVE = "android.genitive";
+ field public static final java.lang.String CASE_INSTRUMENTAL = "android.instrumental";
+ field public static final java.lang.String CASE_LOCATIVE = "android.locative";
+ field public static final java.lang.String CASE_NOMINATIVE = "android.nomative";
+ field public static final java.lang.String CASE_VOCATIVE = "android.vocative";
+ field public static final java.lang.String GENDER_FEMALE = "android.female";
+ field public static final java.lang.String GENDER_MALE = "android.male";
+ field public static final java.lang.String GENDER_NEUTRAL = "android.neutral";
+ field public static final java.lang.String MULTIPLICITY_DUAL = "android.dual";
+ field public static final java.lang.String MULTIPLICITY_PLURAL = "android.plural";
+ field public static final java.lang.String MULTIPLICITY_SINGLE = "android.single";
+ field public static final java.lang.String TYPE_CARDINAL = "android.type.cardinal";
+ field public static final java.lang.String TYPE_TEXT = "android.type.text";
+ }
+
public class TypefaceSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
ctor public TypefaceSpan(java.lang.String);
ctor public TypefaceSpan(android.os.Parcel);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2914a61..f1a2576 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -743,10 +743,13 @@
return Activity.this.findViewById(id);
}
};
-
+
+ // Most recent call to setMediaPlaying().
+ boolean mMediaPlaying;
+
ArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;
LoaderManagerImpl mLoaderManager;
-
+
private static final class ManagedCursor {
ManagedCursor(Cursor cursor) {
mCursor = cursor;
@@ -764,6 +767,7 @@
// protected by synchronized (this)
int mResultCode = RESULT_CANCELED;
Intent mResultData = null;
+
private TranslucentConversionListener mTranslucentCallback;
private boolean mChangeCanvasToTranslucent;
@@ -3704,9 +3708,6 @@
* @see #startActivity
*/
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
- if (options != null) {
- mActivityTransitionState.startExitOutTransition(this, options);
- }
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
@@ -3742,6 +3743,9 @@
mParent.startActivityFromChild(this, intent, requestCode);
}
}
+ if (options != null && !isTopOfTask()) {
+ mActivityTransitionState.startExitOutTransition(this, options);
+ }
}
/**
@@ -5207,9 +5211,8 @@
* another task.
*
* @return true if this is the topmost, non-finishing activity in its task.
- * @hide
*/
- public boolean isTopOfTask() {
+ private boolean isTopOfTask() {
try {
return ActivityManagerNative.getDefault().isTopOfTask(mToken);
} catch (RemoteException e) {
@@ -5325,6 +5328,101 @@
}
/**
+ * Activities that want to show media behind a translucent activity above them must call this
+ * method anytime before a return from {@link #onPause()}. If this call is successful
+ * then the activity should continue to play media when {@link #onPause()} is called, but must
+ * stop playing and release resources prior to or within the call to
+ * {@link #onStopMediaPlaying()}. If this call returns false the activity must stop
+ * playing and release resources immediately.
+ *
+ * <p>Only fullscreen opaque activities may make this call. I.e. this call is a nop
+ * for dialog and translucent activities.
+ *
+ * <p>False will be returned any time this method is call between the return of onPause and
+ * the next call to onResume.
+ *
+ * @param playing true to notify the system that media is starting or continuing playing,
+ * false to indicate that media has stopped or is stopping. Resources must
+ * be released when passing false to this method.
+ * @return the resulting play state. If true the activity may continue playing media beyond
+ * {@link #onPause()}, if false then the caller must stop playing and immediately
+ * release all media resources. Returning false may occur in lieu of a call to
+ * onReleaseMediaResources() so the return value must be checked.
+ *
+ * @see #isBackgroundMediaPlaying()
+ * @see #onStopMediaPlaying()
+ * @see #onBackgroundMediaPlayingChanged(boolean)
+ */
+ public boolean setMediaPlaying(boolean playing) {
+ if (!mResumed) {
+ // Do not permit paused or stopped activities to start playing.
+ playing = false;
+ }
+ try {
+ mMediaPlaying = ActivityManagerNative.getDefault().setMediaPlaying(mToken, playing) &&
+ playing;
+ } catch (RemoteException e) {
+ mMediaPlaying = false;
+ }
+ return mMediaPlaying;
+ }
+
+ /**
+ * Called when a translucent activity over playing media is becoming opaque or another
+ * activity is being launched. Activities that call {@link #setMediaPlaying(boolean)}
+ * must implement this method to at the minimum call
+ * <code>super.onStopMediaPlayback()</code>.
+ *
+ * <p>When this method is called the activity has 500 msec to release the media resources.
+ * If the activity has not returned from this method in 500 msec the system will destroy
+ * the activity and kill the process in order to recover the media resources for another
+ * process. Otherwise {@link #onStop()} will be called following return.
+ *
+ * @see #setMediaPlaying(boolean)
+ * @see #isBackgroundMediaPlaying()
+ * @see #onBackgroundMediaPlayingChanged(boolean)
+ */
+ public void onStopMediaPlaying() {
+ mCalled = true;
+ }
+
+ /**
+ * Translucent activities may call this to determine if there is an activity below it that
+ * is playing media.
+ *
+ * @return true if media is playing according to the most recent call to
+ * {@link #setMediaPlaying(boolean)}, false otherwise.
+ *
+ * @see #setMediaPlaying(boolean)
+ * @see #onStopMediaPlaying()
+ * @see #onBackgroundMediaPlayingChanged(boolean)
+ * @hide
+ */
+ public boolean isBackgroundMediaPlaying() {
+ try {
+ return ActivityManagerNative.getDefault().isBackgroundMediaPlaying(mToken);
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * The topmost foreground activity will receive this call when an activity below it either
+ * starts or stops playing media.
+ *
+ * This call may be a consequence of {@link #setMediaPlaying(boolean)} or might be
+ * due to a background activity finishing itself.
+ *
+ * @param playing true if media playback is starting, false if it is stopping.
+ *
+ * @see #setMediaPlaying(boolean)
+ * @see #isBackgroundMediaPlaying()
+ * @see #onStopMediaPlaying()
+ */
+ public void onBackgroundMediaPlayingChanged(boolean playing) {
+ }
+
+ /**
* Adjust the current immersive mode setting.
*
* Note that changing this value will have no effect on the activity's
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ce36fd8..4a70e15 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2176,6 +2176,33 @@
reply.writeNoException();
return true;
}
+
+ case SET_MEDIA_PLAYING_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean enable = data.readInt() > 0;
+ boolean success = setMediaPlaying(token, enable);
+ reply.writeNoException();
+ reply.writeInt(success ? 1 : 0);
+ return true;
+ }
+
+ case IS_BG_MEDIA_PLAYING_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ final boolean enabled = isBackgroundMediaPlaying(token);
+ reply.writeNoException();
+ reply.writeInt(enabled ? 1 : 0);
+ return true;
+ }
+
+ case MEDIA_RESOURCES_RELEASED: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ mediaResourcesReleased(token);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -5007,5 +5034,46 @@
reply.recycle();
}
+ @Override
+ public boolean setMediaPlaying(IBinder token, boolean playing) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(playing ? 1 : 0);
+ mRemote.transact(SET_MEDIA_PLAYING_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean success = reply.readInt() > 0;
+ data.recycle();
+ reply.recycle();
+ return success;
+ }
+
+ @Override
+ public boolean isBackgroundMediaPlaying(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(IS_BG_MEDIA_PLAYING_TRANSACTION, data, reply, 0);
+ reply.readException();
+ final boolean playing = reply.readInt() > 0;
+ data.recycle();
+ reply.recycle();
+ return playing;
+ }
+
+ @Override
+ public void mediaResourcesReleased(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(MEDIA_RESOURCES_RELEASED, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7b48e1d..184e10c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1153,6 +1153,16 @@
public final void updateTimePrefs(boolean is24Hour) {
DateFormat.set24HourTimePref(is24Hour);
}
+
+ @Override
+ public void scheduleStopMediaPlaying(IBinder token) {
+ sendMessage(H.STOP_MEDIA_PLAYING, token);
+ }
+
+ @Override
+ public void scheduleBackgroundMediaPlayingChanged(IBinder token, boolean playing) {
+ sendMessage(H.BACKGROUND_MEDIA_PLAYING_CHANGED, token, playing ? 1 : 0);
+ }
}
private class H extends Handler {
@@ -1203,6 +1213,8 @@
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
public static final int INSTALL_PROVIDER = 145;
public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
+ public static final int STOP_MEDIA_PLAYING = 147;
+ public static final int BACKGROUND_MEDIA_PLAYING_CHANGED = 148;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1253,6 +1265,8 @@
case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
+ case STOP_MEDIA_PLAYING: return "STOP_MEDIA_PLAYING";
+ case BACKGROUND_MEDIA_PLAYING_CHANGED: return "BACKGROUND_MEDIA_PLAYING_CHANGED";
}
}
return Integer.toString(code);
@@ -1470,6 +1484,11 @@
case ON_NEW_ACTIVITY_OPTIONS:
Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
onNewActivityOptions(pair.first, pair.second);
+ case STOP_MEDIA_PLAYING:
+ handleStopMediaPlaying((IBinder) msg.obj);
+ break;
+ case BACKGROUND_MEDIA_PLAYING_CHANGED:
+ handleOnBackgroundMediaPlayingChanged((IBinder) msg.obj, msg.arg1 > 0);
break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
@@ -2454,6 +2473,34 @@
}
}
+ public void handleStopMediaPlaying(IBinder token) {
+ ActivityClientRecord r = mActivities.get(token);
+ if (r != null) {
+ final Activity activity = r.activity;
+ if (activity.mMediaPlaying) {
+ activity.mCalled = false;
+ activity.onStopMediaPlaying();
+ // Tick, tick, tick. The activity has 500 msec to return or it will be destroyed.
+ if (!activity.mCalled) {
+ throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
+ " did not call through to super.onStopMediaPlayback()");
+ }
+ activity.mMediaPlaying = false;
+ }
+ }
+ try {
+ ActivityManagerNative.getDefault().mediaResourcesReleased(token);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void handleOnBackgroundMediaPlayingChanged(IBinder token, boolean playing) {
+ ActivityClientRecord r = mActivities.get(token);
+ if (r != null) {
+ r.activity.onBackgroundMediaPlayingChanged(playing);
+ }
+ }
+
public void handleInstallProvider(ProviderInfo info) {
installContentProviders(mInitialApplication, Lists.newArrayList(info));
}
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 6c5c98b..1a03fe9 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -274,8 +274,8 @@
return names;
}
- public ArrayList<View> getMappedViews() {
- return mSharedElements;
+ public ArrayList<View> copyMappedViews() {
+ return new ArrayList<View>(mSharedElements);
}
public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
@@ -521,9 +521,7 @@
protected void clearState() {
// Clear the state so that we can't hold any references accidentally and leak memory.
mWindow = null;
- mAllSharedElementNames.clear();
mSharedElements.clear();
- mSharedElementNames.clear();
mTransitioningViews.clear();
mResultReceiver = null;
mPendingTransition = null;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 0304246..0d2af8c 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -240,7 +240,7 @@
if (!mHasExited) {
mHasExited = true;
if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.stop();
+ mEnterTransitionCoordinator.cancelEnter();
mEnterTransitionCoordinator = null;
}
ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
@@ -268,7 +268,7 @@
if (mCalledExitCoordinator != null) {
mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
mExitingTo = mCalledExitCoordinator.getMappedNames();
- mExitingToView = mCalledExitCoordinator.getMappedViews();
+ mExitingToView = mCalledExitCoordinator.copyMappedViews();
mCalledExitCoordinator.startExit();
}
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 6dead08..0b4510fe 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -647,6 +647,25 @@
reply.writeNoException();
return true;
}
+
+ case STOP_MEDIA_PLAYING_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ scheduleStopMediaPlaying(token);
+ reply.writeNoException();
+ return true;
+ }
+
+ case BACKGROUND_MEDIA_PLAYING_CHANGED_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean enabled = data.readInt() > 0;
+ scheduleBackgroundMediaPlayingChanged(token, enabled);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -1304,4 +1323,23 @@
mRemote.transact(UPDATE_TIME_PREFS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ @Override
+ public void scheduleStopMediaPlaying(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(STOP_MEDIA_PLAYING_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
+ @Override
+ public void scheduleBackgroundMediaPlayingChanged(IBinder token, boolean enabled) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeStrongBinder(token);
+ data.writeInt(enabled ? 1 : 0);
+ mRemote.transact(BACKGROUND_MEDIA_PLAYING_CHANGED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 5e18d0f..90f36b8 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -58,6 +58,7 @@
private Bundle mSharedElementsBundle;
private boolean mWasOpaque;
private boolean mAreViewsReady;
+ private boolean mIsViewsTransitionStarted;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -219,7 +220,7 @@
private void cancel() {
if (!mIsCanceled) {
mIsCanceled = true;
- if (getViewsTransition() == null) {
+ if (getViewsTransition() == null || mIsViewsTransitionStarted) {
setViewVisibility(mSharedElements, View.VISIBLE);
} else {
mTransitioningViews.addAll(mSharedElements);
@@ -363,6 +364,7 @@
stripOffscreenViews();
}
}
+ mIsViewsTransitionStarted = mIsViewsTransitionStarted || startEnterTransition;
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
if (startSharedElementTransition) {
@@ -433,6 +435,17 @@
public void stop() {
makeOpaque();
+ mIsCanceled = true;
+ mResultReceiver = null;
+ if (mBackgroundAnimator != null) {
+ mBackgroundAnimator.end();
+ mBackgroundAnimator = null;
+ }
+ mActivity = null;
+ clearState();
+ }
+
+ public void cancelEnter() {
mHasStopped = true;
mIsCanceled = true;
mResultReceiver = null;
@@ -440,6 +453,8 @@
mBackgroundAnimator.cancel();
mBackgroundAnimator = null;
}
+ mActivity = null;
+ clearState();
}
private void makeOpaque() {
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 93cba0d..0fbd685 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -66,6 +66,8 @@
private Bundle mExitSharedElementBundle;
+ private boolean mIsExitStarted;
+
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
super(activity.getWindow(), names, getListener(activity, isReturning),
@@ -114,6 +116,7 @@
setViewVisibility(mTransitioningViews, View.VISIBLE);
setViewVisibility(mSharedElements, View.VISIBLE);
mIsHidden = true;
+ clearState();
}
private void sharedElementExitBack() {
@@ -164,46 +167,52 @@
}
public void startExit() {
- startTransition(new Runnable() {
- @Override
- public void run() {
- beginTransitions();
- }
- });
- setViewVisibility(mTransitioningViews, View.INVISIBLE);
+ if (!mIsExitStarted) {
+ mIsExitStarted = true;
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ beginTransitions();
+ }
+ });
+ setViewVisibility(mTransitioningViews, View.INVISIBLE);
+ }
}
public void startExit(int resultCode, Intent data) {
- mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- mIsCanceled = true;
- finish();
- }
- };
- mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
- if (getDecor().getBackground() == null) {
- ColorDrawable black = new ColorDrawable(0xFF000000);
- black.setAlpha(0);
- getWindow().setBackgroundDrawable(black);
- black.setAlpha(255);
- }
- ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
- mAllSharedElementNames, resultCode, data);
- mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
- @Override
- public void onTranslucentConversionComplete(boolean drawComplete) {
- if (!mIsCanceled) {
- fadeOutBackground();
+ if (!mIsExitStarted) {
+ mIsExitStarted = true;
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ mIsCanceled = true;
+ finish();
}
+ };
+ mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+ if (getDecor().getBackground() == null) {
+ ColorDrawable black = new ColorDrawable(0xFF000000);
+ black.setAlpha(0);
+ getWindow().setBackgroundDrawable(black);
+ black.setAlpha(255);
}
- }, options);
- startTransition(new Runnable() {
- @Override
- public void run() {
- startExitTransition();
- }
- });
+ ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
+ mAllSharedElementNames, resultCode, data);
+ mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
+ @Override
+ public void onTranslucentConversionComplete(boolean drawComplete) {
+ if (!mIsCanceled) {
+ fadeOutBackground();
+ }
+ }
+ }, options);
+ startTransition(new Runnable() {
+ @Override
+ public void run() {
+ startExitTransition();
+ }
+ });
+ }
}
private void startExitTransition() {
@@ -216,20 +225,23 @@
private void fadeOutBackground() {
if (mBackgroundAnimator == null) {
- Drawable background = getDecor().getBackground();
- mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
- mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackgroundAnimator = null;
- if (!mIsCanceled) {
- mIsBackgroundReady = true;
- notifyComplete();
+ ViewGroup decor = getDecor();
+ Drawable background;
+ if (decor != null && (background = decor.getBackground()) != null) {
+ mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
+ mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBackgroundAnimator = null;
+ if (!mIsCanceled) {
+ mIsBackgroundReady = true;
+ notifyComplete();
+ }
}
- }
- });
- mBackgroundAnimator.setDuration(getFadeDuration());
- mBackgroundAnimator.start();
+ });
+ mBackgroundAnimator.setDuration(getFadeDuration());
+ mBackgroundAnimator.start();
+ }
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6e4192b..b3a8a8a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -415,11 +415,9 @@
public void performIdleMaintenance() throws RemoteException;
- /** @hide */
public IActivityContainer createActivityContainer(IBinder parentActivityToken,
IActivityContainerCallback callback) throws RemoteException;
- /** @hide */
public void deleteActivityContainer(IActivityContainer container) throws RemoteException;
public IActivityContainer getEnclosingActivityContainer(IBinder activityToken)
@@ -427,28 +425,25 @@
public IBinder getHomeActivityToken() throws RemoteException;
- /** @hide */
public void startLockTaskModeOnCurrent() throws RemoteException;
- /** @hide */
public void startLockTaskMode(int taskId) throws RemoteException;
- /** @hide */
public void startLockTaskMode(IBinder token) throws RemoteException;
- /** @hide */
public void stopLockTaskMode() throws RemoteException;
- /** @hide */
public void stopLockTaskModeOnCurrent() throws RemoteException;
- /** @hide */
public boolean isInLockTaskMode() throws RemoteException;
- /** @hide */
public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
throws RemoteException;
+ public boolean setMediaPlaying(IBinder token, boolean playing) throws RemoteException;
+ public boolean isBackgroundMediaPlaying(IBinder token) throws RemoteException;
+ public void mediaResourcesReleased(IBinder token) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -753,4 +748,7 @@
int STOP_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
int FINISH_VOICE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+223;
int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
+ int SET_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+225;
+ int IS_BG_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+226;
+ int MEDIA_RESOURCES_RELEASED = IBinder.FIRST_CALL_TRANSACTION+227;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index d3c4854..18faf0e 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -145,6 +145,8 @@
void setProcessState(int state) throws RemoteException;
void scheduleInstallProvider(ProviderInfo provider) throws RemoteException;
void updateTimePrefs(boolean is24Hour) throws RemoteException;
+ void scheduleStopMediaPlaying(IBinder token) throws RemoteException;
+ void scheduleBackgroundMediaPlayingChanged(IBinder token, boolean enabled) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
@@ -199,4 +201,6 @@
int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
int SCHEDULE_INSTALL_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50;
int UPDATE_TIME_PREFS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+51;
+ int STOP_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+52;
+ int BACKGROUND_MEDIA_PLAYING_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c5eb356..61ed631 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -744,13 +744,6 @@
public static final String EXTRA_PEOPLE = "android.people";
/**
- * @hide
- * Extra added by NotificationManagerService to indicate whether
- * the Notifications's score has been modified.
- */
- public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified";
-
- /**
* {@link #extras} key: used to provide hints about the appropriateness of
* displaying this notification as a heads-up notification.
* @hide
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d75304f..35e5050 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -20,6 +20,7 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
@@ -37,6 +38,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -504,13 +506,7 @@
*/
public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
// TODO: Return null if this feature is not supported by hardware.
- try {
- IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
- return new BluetoothLeAdvertiser(iGatt);
- } catch (RemoteException e) {
- Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e);
- return null;
- }
+ return new BluetoothLeAdvertiser(mManagerService);
}
/**
@@ -518,13 +514,7 @@
*/
public BluetoothLeScanner getBluetoothLeScanner() {
// TODO: Return null if BLE scan is not supported by hardware.
- try {
- IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
- return new BluetoothLeScanner(iGatt);
- } catch (RemoteException e) {
- Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e);
- return null;
- }
+ return new BluetoothLeScanner(mManagerService);
}
/**
@@ -2137,5 +2127,10 @@
public void onConnectionCongested(String address, boolean congested) {
// no op
}
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ // no op
+ }
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 6c1a45e..4255cd4 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -627,6 +628,11 @@
Log.w(TAG, "Unhandled exception in callback", ex);
}
}
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ // no op
+ }
};
/*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 273d76d..5ed10a6 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -38,6 +38,7 @@
void startScanWithFilters(in int appIf, in boolean isServer,
in ScanSettings settings, in List<ScanFilter> filters);
void stopScan(in int appIf, in boolean isServer);
+ void flushPendingBatchResults(in int appIf, in boolean isServer);
void startMultiAdvertising(in int appIf,
in AdvertisementData advertiseData,
in AdvertisementData scanResponse,
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index 946a6f6..c18d357 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -16,7 +16,7 @@
package android.bluetooth;
import android.os.ParcelUuid;
-
+import android.bluetooth.le.ScanResult;
/**
* Callback definitions for interacting with BLE / GATT
@@ -27,6 +27,7 @@
void onClientConnectionState(in int status, in int clientIf,
in boolean connected, in String address);
void onScanResult(in String address, in int rssi, in byte[] advData);
+ void onBatchScanResults(in List<ScanResult> batchResults);
void onGetService(in String address, in int srvcType, in int srvcInstId,
in ParcelUuid srvcUuid);
void onGetIncludedService(in String address, in int srvcType, in int srvcInstId,
diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java
index f1334c2..59f8cdc 100644
--- a/core/java/android/bluetooth/le/AdvertiseCallback.java
+++ b/core/java/android/bluetooth/le/AdvertiseCallback.java
@@ -52,6 +52,12 @@
public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5;
/**
+ * Operation fails due to GATT service failure.
+ * @hide
+ */
+ public static final int ADVERTISE_FAILED_GATT_SERVICE_FAILURE = 6;
+
+ /**
* Callback when advertising operation succeeds.
*
* @param settingsInEffect The actual settings used for advertising, which may be different from
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index c20b81b..a83d875 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -20,6 +20,7 @@
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothManager;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -27,6 +28,7 @@
import android.util.Log;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -47,7 +49,7 @@
private static final String TAG = "BluetoothLeAdvertiser";
- private final IBluetoothGatt mBluetoothGatt;
+ private final IBluetoothManager mBluetoothManager;
private final Handler mHandler;
private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
@@ -55,11 +57,11 @@
/**
* Use BluetoothAdapter.getLeAdvertiser() instead.
*
- * @param bluetoothGatt
+ * @param bluetoothManager
* @hide
*/
- public BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
- mBluetoothGatt = bluetoothGatt;
+ public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) {
+ mBluetoothManager = bluetoothManager;
mHandler = new Handler(Looper.getMainLooper());
}
@@ -102,11 +104,19 @@
postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
return;
}
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get bluetooth gatt - ", e);
+ postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_CONTROLLER_FAILURE);
+ return;
+ }
AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
- scanResponse, settings, mBluetoothGatt);
+ scanResponse, settings, gatt);
UUID uuid = UUID.randomUUID();
try {
- mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ gatt.registerClient(new ParcelUuid(uuid), wrapper);
if (wrapper.advertiseStarted()) {
mLeAdvertisers.put(callback, wrapper);
}
@@ -133,12 +143,19 @@
return;
}
try {
- mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
+ IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt();
+ if (gatt == null) {
+ postCallbackFailure(callback,
+ AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE);
+ }
+ gatt.stopMultiAdvertising(wrapper.mLeHandle);
if (wrapper.advertiseStopped()) {
mLeAdvertisers.remove(callback);
}
} catch (RemoteException e) {
Log.e(TAG, "failed to stop advertising", e);
+ postCallbackFailure(callback,
+ AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE);
}
}
@@ -214,8 +231,6 @@
Log.e(TAG, "fail to start le advertise: " + e);
mLeHandle = -1;
notifyAll();
- } catch (Exception e) {
- Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
}
} else {
// registration failed
@@ -360,6 +375,11 @@
public void onConnectionCongested(String address, boolean congested) {
// no op
}
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ // no op
+ }
}
private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index fbaf5d2..44aa117 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothManager;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -51,15 +52,15 @@
private static final String TAG = "BluetoothLeScanner";
private static final boolean DBG = true;
- private final IBluetoothGatt mBluetoothGatt;
+ private final IBluetoothManager mBluetoothManager;
private final Handler mHandler;
private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
/**
* @hide
*/
- public BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
- mBluetoothGatt = bluetoothGatt;
+ public BluetoothLeScanner(IBluetoothManager bluetoothManager) {
+ mBluetoothManager = bluetoothManager;
mHandler = new Handler(Looper.getMainLooper());
mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
}
@@ -84,11 +85,21 @@
postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
return;
}
- BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ gatt = null;
+ }
+ if (gatt == null) {
+ postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE);
+ return;
+ }
+ BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
settings, callback);
try {
UUID uuid = UUID.randomUUID();
- mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ gatt.registerClient(new ParcelUuid(uuid), wrapper);
if (wrapper.scanStarted()) {
mLeScanClients.put(callback, wrapper);
} else {
@@ -131,18 +142,26 @@
}
/**
- * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
- * batched on bluetooth controller.
+ * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
+ * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
+ * will be delivered through the {@code callback}.
*
* @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
* used to start scan.
- * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
- * get batch scan callback if the batch scan buffer is flushed.
- * @return Batch Scan results.
- * @hide TODO: unhide when batching is supported in stack.
+ *
+ * @hide
*/
- public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
- throw new UnsupportedOperationException("not impelemented");
+ public void flushPendingScanResults(ScanCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null!");
+ }
+ synchronized (mLeScanClients) {
+ BleScanCallbackWrapper wrapper = mLeScanClients.get(callback);
+ if (wrapper == null) {
+ return;
+ }
+ wrapper.flushPendingBatchResults();
+ }
}
/**
@@ -195,13 +214,27 @@
mBluetoothGatt.stopScan(mLeHandle, false);
mBluetoothGatt.unregisterClient(mLeHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to stop scan and unregister" + e);
+ Log.e(TAG, "Failed to stop scan and unregister", e);
}
mLeHandle = -1;
notifyAll();
}
}
+ void flushPendingBatchResults() {
+ synchronized (this) {
+ if (mLeHandle <= 0) {
+ Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+ return;
+ }
+ try {
+ mBluetoothGatt.flushPendingBatchResults(mLeHandle, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get pending scan results", e);
+ }
+ }
+ }
+
/**
* Application interface registered - app is ready to go
*/
@@ -256,9 +289,27 @@
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
address);
long scanNanos = SystemClock.elapsedRealtimeNanos();
- ScanResult result = new ScanResult(device, advData, rssi,
+ final ScanResult result = new ScanResult(device, advData, rssi,
scanNanos);
- mScanCallback.onAdvertisementUpdate(result);
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ mScanCallback.onAdvertisementUpdate(result);
+ }
+ });
+
+ }
+
+ @Override
+ public void onBatchScanResults(final List<ScanResult> results) {
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ mScanCallback.onBatchScanResults(results);
+ }
+ });
}
@Override
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 0a85675..3bb39b3 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -44,7 +44,6 @@
public static final int CALLBACK_TYPE_ON_UPDATE = 0;
/**
* Callback when a bluetooth advertisement is found for the first time.
- *
* @hide
*/
public static final int CALLBACK_TYPE_ON_FOUND = 1;
@@ -190,6 +189,7 @@
* {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or
* {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}.
* @throws IllegalArgumentException If the {@code scanResultType} is invalid.
+ *
* @hide
*/
public Builder setScanResultType(int scanResultType) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 3ef538c..d505365 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -85,7 +85,9 @@
* being set to true.
*/
static final boolean LIE_ABOUT_AE_STATE = true;
+ static final boolean LIE_ABOUT_AE_MAX_REGIONS = true;
static final boolean LIE_ABOUT_AF = true;
+ static final boolean LIE_ABOUT_AF_MAX_REGIONS = true;
static final boolean LIE_ABOUT_AWB = true;
/**
@@ -365,12 +367,23 @@
/*
* android.control.maxRegions
*/
+ final int AE = 0, AWB = 1, AF = 2;
+
int[] maxRegions = new int[3];
- maxRegions[0] = p.getMaxNumMeteringAreas();
- maxRegions[1] = 0; // AWB regions not supported in API1
- maxRegions[2] = p.getMaxNumFocusAreas();
+ maxRegions[AE] = p.getMaxNumMeteringAreas();
+ maxRegions[AWB] = 0; // AWB regions not supported in API1
+ maxRegions[AF] = p.getMaxNumFocusAreas();
+
+ if (LIE_ABOUT_AE_MAX_REGIONS) {
+ maxRegions[AE] = 0;
+ }
+ if (LIE_ABOUT_AF_MAX_REGIONS) {
+ maxRegions[AF] = 0;
+ }
+
m.set(CONTROL_MAX_REGIONS, maxRegions);
- // TODO
+
+ // TODO rest of control fields
}
private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index 8fbd41c..79287c1 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -121,6 +121,17 @@
// TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED);
}
+
+ // control.aeRegions
+
+ /*
+ * TODO: Use the *resulting* crop region to calculate intersection with
+ * metering region
+ *
+ * Report the sensor-relative metering region in the result even
+ * if that's not actually the real thing (similar to how we do it
+ * for zooming)
+ */
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 2d7af85..7a4e5a5 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,8 +16,6 @@
package android.hardware.soundtrigger;
-import android.content.Context;
-import android.content.Intent;
import android.os.Handler;
import java.util.ArrayList;
@@ -65,7 +63,7 @@
public final int maxSoundModels;
/** Maximum number of key phrases */
- public final int maxKeyPhrases;
+ public final int maxKeyphrases;
/** Maximum number of users per key phrase */
public final int maxUsers;
@@ -86,7 +84,7 @@
public final int powerConsumptionMw;
ModuleProperties(int id, String implementor, String description,
- String uuid, int version, int maxSoundModels, int maxKeyPhrases,
+ String uuid, int version, int maxSoundModels, int maxKeyphrases,
int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
int maxBufferMs, boolean supportsConcurrentCapture,
int powerConsumptionMw) {
@@ -96,7 +94,7 @@
this.uuid = UUID.fromString(uuid);
this.version = version;
this.maxSoundModels = maxSoundModels;
- this.maxKeyPhrases = maxKeyPhrases;
+ this.maxKeyphrases = maxKeyphrases;
this.maxUsers = maxUsers;
this.recognitionModes = recognitionModes;
this.supportsCaptureTransition = supportsCaptureTransition;
@@ -109,7 +107,7 @@
/*****************************************************************************
* A SoundModel describes the attributes and contains the binary data used by the hardware
* implementation to detect a particular sound pattern.
- * A specialized version {@link KeyPhraseSoundModel} is defined for key phrase
+ * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
* sound models.
****************************************************************************/
public static class SoundModel {
@@ -119,23 +117,30 @@
/** Keyphrase sound model */
public static final int TYPE_KEYPHRASE = 0;
+ /** Unique sound model identifier */
+ public final UUID uuid;
+
/** Sound model type (e.g. TYPE_KEYPHRASE); */
public final int type;
/** Opaque data. For use by vendor implementation and enrollment application */
public final byte[] data;
- public SoundModel(int type, byte[] data) {
+ public SoundModel(UUID uuid, int type, byte[] data) {
+ this.uuid = uuid;
this.type = type;
this.data = data;
}
}
/*****************************************************************************
- * A KeyPhrase describes a key phrase that can be detected by a
- * {@link KeyPhraseSoundModel}
+ * A Keyphrase describes a key phrase that can be detected by a
+ * {@link KeyphraseSoundModel}
****************************************************************************/
- public static class KeyPhrase {
+ public static class Keyphrase {
+ /** Unique identifier for this keyphrase */
+ public final int id;
+
/** Recognition modes supported for this key phrase in the model */
public final int recognitionModes;
@@ -145,29 +150,31 @@
/** Key phrase text */
public final String text;
- /** Number of users this key phrase has been trained for */
- public final int numUsers;
+ /** Users this key phrase has been trained for. countains sound trigger specific user IDs
+ * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
+ public final int[] users;
- public KeyPhrase(int recognitionModes, String locale, String text, int numUsers) {
+ public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) {
+ this.id = id;
this.recognitionModes = recognitionModes;
this.locale = locale;
this.text = text;
- this.numUsers = numUsers;
+ this.users = users;
}
}
/*****************************************************************************
- * A KeyPhraseSoundModel is a specialized {@link SoundModel} for key phrases.
+ * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
* It contains data needed by the hardware to detect a certain number of key phrases
- * and the list of corresponding {@link KeyPhrase} descriptors.
+ * and the list of corresponding {@link Keyphrase} descriptors.
****************************************************************************/
- public static class KeyPhraseSoundModel extends SoundModel {
+ public static class KeyphraseSoundModel extends SoundModel {
/** Key phrases in this sound model */
- public final KeyPhrase[] keyPhrases; // keyword phrases in model
+ public final Keyphrase[] keyphrases; // keyword phrases in model
- public KeyPhraseSoundModel(byte[] data, KeyPhrase[] keyPhrases) {
- super(TYPE_KEYPHRASE, data);
- this.keyPhrases = keyPhrases;
+ public KeyphraseSoundModel(UUID id, byte[] data, Keyphrase[] keyphrases) {
+ super(id, TYPE_KEYPHRASE, data);
+ this.keyphrases = keyphrases;
}
}
@@ -209,55 +216,106 @@
/** Delay in ms between end of model detection and start of audio available for capture.
* A negative value is possible (e.g. if keyphrase is also available for capture) */
public final int captureDelayMs;
+ /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+ public final int capturePreambleMs;
/** Opaque data for use by system applications who know about voice engine internals,
* typically during enrollment. */
public final byte[] data;
RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
- int captureSession, int captureDelayMs, byte[] data) {
+ int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data) {
this.status = status;
this.soundModelHandle = soundModelHandle;
this.captureAvailable = captureAvailable;
this.captureSession = captureSession;
this.captureDelayMs = captureDelayMs;
+ this.capturePreambleMs = capturePreambleMs;
this.data = data;
}
}
/**
- * Additional data conveyed by a {@link KeyPhraseRecognitionEvent}
+ * A RecognitionConfig is provided to
+ * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
+ * recognition request.
+ */
+ public static class RecognitionConfig {
+ /** True if the DSP should capture the trigger sound and make it available for further
+ * capture. */
+ public final boolean captureRequested;
+ /** List of all keyphrases in the sound model for which recognition should be performed with
+ * options for each keyphrase. */
+ public final KeyphraseRecognitionExtra keyphrases[];
+ /** Opaque data for use by system applications who know about voice engine internals,
+ * typically during enrollment. */
+ public final byte[] data;
+
+ public RecognitionConfig(boolean captureRequested,
+ KeyphraseRecognitionExtra keyphrases[], byte[] data) {
+ this.captureRequested = captureRequested;
+ this.keyphrases = keyphrases;
+ this.data = data;
+ }
+ }
+
+ /**
+ * Confidence level for users defined in a keyphrase.
+ * - The confidence level is expressed in percent (0% -100%).
+ * When used in a {@link KeyphraseRecognitionEvent} it indicates the detected confidence level
+ * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
+ * should trigger a recognition.
+ * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
+ */
+ public static class ConfidenceLevel {
+ public final int userId;
+ public final int confidenceLevel;
+
+ public ConfidenceLevel(int userId, int confidenceLevel) {
+ this.userId = userId;
+ this.confidenceLevel = confidenceLevel;
+ }
+ }
+
+ /**
+ * Additional data conveyed by a {@link KeyphraseRecognitionEvent}
* for a key phrase detection.
*/
- public static class KeyPhraseRecognitionExtra {
- /** Confidence level for each user defined in the key phrase in the same order as
- * users in the key phrase. The confidence level is expressed in percentage (0% -100%) */
- public final int[] confidenceLevels;
+ public static class KeyphraseRecognitionExtra {
+ /** The keyphrse ID */
+ public final int id;
/** Recognition modes matched for this event */
public final int recognitionModes;
- KeyPhraseRecognitionExtra(int[] confidenceLevels, int recognitionModes) {
- this.confidenceLevels = confidenceLevels;
+ /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
+ * be recognized (RecognitionConfig) */
+ public final ConfidenceLevel[] confidenceLevels;
+
+ public KeyphraseRecognitionExtra(int id, int recognitionModes,
+ ConfidenceLevel[] confidenceLevels) {
+ this.id = id;
this.recognitionModes = recognitionModes;
+ this.confidenceLevels = confidenceLevels;
}
}
/**
* Specialized {@link RecognitionEvent} for a key phrase detection.
*/
- public static class KeyPhraseRecognitionEvent extends RecognitionEvent {
+ public static class KeyphraseRecognitionEvent extends RecognitionEvent {
/** Indicates if the key phrase is present in the buffered audio available for capture */
- public final KeyPhraseRecognitionExtra[] keyPhraseExtras;
+ public final KeyphraseRecognitionExtra[] keyphraseExtras;
/** Additional data available for each recognized key phrases in the model */
- public final boolean keyPhraseInCapture;
+ public final boolean keyphraseInCapture;
- KeyPhraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
- int captureSession, int captureDelayMs, byte[] data,
- boolean keyPhraseInCapture, KeyPhraseRecognitionExtra[] keyPhraseExtras) {
- super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, data);
- this.keyPhraseInCapture = keyPhraseInCapture;
- this.keyPhraseExtras = keyPhraseExtras;
+ KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data,
+ boolean keyphraseInCapture, KeyphraseRecognitionExtra[] keyphraseExtras) {
+ super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
+ capturePreambleMs, data);
+ this.keyphraseInCapture = keyphraseInCapture;
+ this.keyphraseExtras = keyphraseExtras;
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 776f85d..4a54fd8 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -94,7 +94,8 @@
* Recognition must be restarted after each callback (success or failure) received on
* the {@link SoundTrigger.StatusListener}.
* @param soundModelHandle The sound model handle to start listening to
- * @param data Opaque data for use by the implementation for this recognition
+ * @param config contains configuration information for this recognition request:
+ * recognition mode, keyphrases, users, minimum confidence levels...
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
@@ -105,7 +106,7 @@
* service fails
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
*/
- public native int startRecognition(int soundModelHandle, byte[] data);
+ public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config);
/**
* Stop listening to all key phrases in a {@link SoundTrigger.SoundModel}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0297c84..fb4912f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -491,11 +491,17 @@
*/
public static final int TYPE_PROXY = 16;
- /** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_PROXY;
+ /**
+ * A virtual network using one or more native bearers.
+ * It may or may not be providing security services.
+ */
+ public static final int TYPE_VPN = 17;
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_PROXY;
+ public static final int MAX_RADIO_TYPE = TYPE_VPN;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_VPN;
/**
* If you want to set the default network preference,you can directly
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
deleted file mode 100644
index eff9f9f..0000000
--- a/core/java/android/net/DummyDataStateTracker.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2010 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.net;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.util.Slog;
-
-/**
- * A dummy data state tracker for use when we don't have a real radio
- * connection. useful when bringing up a board or when you have network
- * access through other means.
- *
- * {@hide}
- */
-public class DummyDataStateTracker extends BaseNetworkStateTracker {
-
- private static final String TAG = "DummyDataStateTracker";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private boolean mTeardownRequested = false;
- private Handler mTarget;
- private boolean mPrivateDnsRouteSet = false;
- private boolean mDefaultRouteSet = false;
-
- // DEFAULT and HIPRI are the same connection. If we're one of these we need to check if
- // the other is also disconnected before we reset sockets
- private boolean mIsDefaultOrHipri = false;
-
- /**
- * Create a new DummyDataStateTracker
- * @param netType the ConnectivityManager network type
- * @param tag the name of this network
- */
- public DummyDataStateTracker(int netType, String tag) {
- mNetworkInfo = new NetworkInfo(netType);
- }
-
- /**
- * Begin monitoring data connectivity.
- *
- * @param context is the current Android context
- * @param target is the Handler to which to return the events.
- */
- public void startMonitoring(Context context, Handler target) {
- mTarget = target;
- mContext = context;
- }
-
- public boolean isPrivateDnsRouteSet() {
- return mPrivateDnsRouteSet;
- }
-
- public void privateDnsRouteSet(boolean enabled) {
- mPrivateDnsRouteSet = enabled;
- }
-
- public NetworkInfo getNetworkInfo() {
- return mNetworkInfo;
- }
-
- public boolean isDefaultRouteSet() {
- return mDefaultRouteSet;
- }
-
- public void defaultRouteSet(boolean enabled) {
- mDefaultRouteSet = enabled;
- }
-
- /**
- * This is not implemented.
- */
- public void releaseWakeLock() {
- }
-
- /**
- * Report whether data connectivity is possible.
- */
- public boolean isAvailable() {
- return true;
- }
-
- /**
- * Return the system properties name associated with the tcp buffer sizes
- * for this network.
- */
- public String getTcpBufferSizesPropName() {
- return "net.tcp.buffersize.unknown";
- }
-
- /**
- * Tear down mobile data connectivity, i.e., disable the ability to create
- * mobile data connections.
- * TODO - make async and return nothing?
- */
- public boolean teardown() {
- setDetailedState(NetworkInfo.DetailedState.DISCONNECTING, "disabled", null);
- setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "disabled", null);
- return true;
- }
-
- @Override
- public void captivePortalCheckCompleted(boolean isCaptivePortal) {
- // not implemented
- }
-
- /**
- * Record the detailed state of a network, and if it is a
- * change from the previous state, send a notification to
- * any listeners.
- * @param state the new {@code DetailedState}
- * @param reason a {@code String} indicating a reason for the state change,
- * if one was supplied. May be {@code null}.
- * @param extraInfo optional {@code String} providing extra information about the state change
- */
- private void setDetailedState(NetworkInfo.DetailedState state, String reason,
- String extraInfo) {
- if (DBG) log("setDetailed state, old ="
- + mNetworkInfo.getDetailedState() + " and new state=" + state);
- mNetworkInfo.setDetailedState(state, reason, extraInfo);
- Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
-
- public void setTeardownRequested(boolean isRequested) {
- mTeardownRequested = isRequested;
- }
-
- public boolean isTeardownRequested() {
- return mTeardownRequested;
- }
-
- /**
- * Re-enable mobile data connectivity after a {@link #teardown()}.
- * TODO - make async and always get a notification?
- */
- public boolean reconnect() {
- setDetailedState(NetworkInfo.DetailedState.CONNECTING, "enabled", null);
- setDetailedState(NetworkInfo.DetailedState.CONNECTED, "enabled", null);
- setTeardownRequested(false);
- return true;
- }
-
- /**
- * Turn on or off the mobile radio. No connectivity will be possible while the
- * radio is off. The operation is a no-op if the radio is already in the desired state.
- * @param turnOn {@code true} if the radio should be turned on, {@code false} if
- */
- public boolean setRadio(boolean turnOn) {
- return true;
- }
-
- @Override
- public void setUserDataEnable(boolean enabled) {
- // ignored
- }
-
- @Override
- public void setPolicyDataEnable(boolean enabled) {
- // ignored
- }
-
- @Override
- public String toString() {
- StringBuffer sb = new StringBuffer("Dummy data state: none, dummy!");
- return sb.toString();
- }
-
- /**
- * @see android.net.NetworkStateTracker#getLinkProperties()
- */
- public LinkProperties getLinkProperties() {
- return new LinkProperties(mLinkProperties);
- }
-
- public void setDependencyMet(boolean met) {
- // not supported on this network
- }
-
- @Override
- public void addStackedLink(LinkProperties link) {
- mLinkProperties.addStackedLink(link);
- }
-
- @Override
- public void removeStackedLink(LinkProperties link) {
- mLinkProperties.removeStackedLink(link);
- }
-
- @Override
- public void supplyMessenger(Messenger messenger) {
- // not supported on this network
- }
-
- static private void log(String s) {
- Slog.d(TAG, s);
- }
-
- static private void loge(String s) {
- Slog.e(TAG, s);
- }
-}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 48122d6..1d1aafb 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -43,6 +43,7 @@
import android.text.style.SuggestionSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.TextAppearanceSpan;
+import android.text.style.TtsSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
@@ -607,7 +608,9 @@
/** @hide */
public static final int LOCALE_SPAN = 23;
/** @hide */
- public static final int LAST_SPAN = LOCALE_SPAN;
+ public static final int TTS_SPAN = 24;
+ /** @hide */
+ public static final int LAST_SPAN = TTS_SPAN;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
@@ -786,6 +789,10 @@
readSpan(p, sp, new LocaleSpan(p));
break;
+ case TTS_SPAN:
+ readSpan(p, sp, new TtsSpan(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
new file mode 100644
index 0000000..ab736c5
--- /dev/null
+++ b/core/java/android/text/style/TtsSpan.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+
+/**
+ * A span that supplies addition meta-data intended for text-to-speech rendering
+ * of the associated text. If the text is being processed by a text-to-speech
+ * engine, the engine may use the data in this span in addition to or instead of
+ * its associated text.
+ */
+public class TtsSpan implements ParcelableSpan {
+ private final String mType;
+ private final PersistableBundle mArgs;
+
+ /**
+ * This span type can be used to add morphosyntactic features to the text it
+ * spans over, or synthesize a something else than the spanned text. Use
+ * the argument {@link #ARG_TEXT} to set a different text.
+ * Accepts the arguments {@link TtsSpan#ARG_GENDER},
+ * {@link TtsSpan#ARG_ANIMACY}, {@link TtsSpan#ARG_MULTIPLICITY} and
+ * {@link TtsSpan#ARG_CASE}.
+ */
+ public static final String TYPE_TEXT = "android.type.text";
+
+ /**
+ * The text associated with this span is a cardinal. Must include the
+ * number to be synthesized with {@link #ARG_NUMBER}.
+ * Also accepts the arguments {@link TtsSpan#ARG_GENDER},
+ * {@link TtsSpan#ARG_ANIMACY}, {@link TtsSpan#ARG_MULTIPLICITY} and
+ * {@link TtsSpan#ARG_CASE}.
+ */
+ public static final String TYPE_CARDINAL = "android.type.cardinal";
+
+ /**
+ * String supplying the text to be synthesized. The synthesizer is free
+ * to decide how to interpret the text.
+ */
+ public static final String ARG_TEXT = "android.arg.text";
+
+ /**
+ * Argument used to specify a whole number. The value can be a string of
+ * digits of any size optionally prefixed with a - or +.
+ */
+ public static final String ARG_NUMBER = "android.arg.number";
+
+ /**
+ * String argument supplying gender information. Can be any of
+ * {@link TtsSpan#GENDER_NEUTRAL}, {@link TtsSpan#GENDER_MALE} and
+ * {@link TtsSpan#GENDER_FEMALE}.
+ */
+ public static final String ARG_GENDER = "android.arg.gender";
+
+ public static final String GENDER_NEUTRAL = "android.neutral";
+ public static final String GENDER_MALE = "android.male";
+ public static final String GENDER_FEMALE = "android.female";
+
+ /**
+ * String argument supplying animacy information. Can be
+ * {@link TtsSpan#ANIMACY_ANIMATE} or
+ * {@link TtsSpan#ANIMACY_INANIMATE}
+ */
+ public static final String ARG_ANIMACY = "android.arg.animacy";
+
+ public static final String ANIMACY_ANIMATE = "android.animate";
+ public static final String ANIMACY_INANIMATE = "android.inanimate";
+
+ /**
+ * String argument supplying multiplicity information. Can be any of
+ * {@link TtsSpan#MULTIPLICITY_SINGLE},
+ * {@link TtsSpan#MULTIPLICITY_DUAL} and
+ * {@link TtsSpan#MULTIPLICITY_PLURAL}
+ */
+ public static final String ARG_MULTIPLICITY = "android.arg.multiplicity";
+
+ public static final String MULTIPLICITY_SINGLE = "android.single";
+ public static final String MULTIPLICITY_DUAL = "android.dual";
+ public static final String MULTIPLICITY_PLURAL = "android.plural";
+
+ /**
+ * String argument supplying case information. Can be any of
+ * {@link TtsSpan#CASE_NOMINATIVE}, {@link TtsSpan#CASE_ACCUSATIVE},
+ * {@link TtsSpan#CASE_DATIVE}, {@link TtsSpan#CASE_ABLATIVE},
+ * {@link TtsSpan#CASE_GENITIVE}, {@link TtsSpan#CASE_VOCATIVE},
+ * {@link TtsSpan#CASE_LOCATIVE} and
+ * {@link TtsSpan#CASE_INSTRUMENTAL}
+ */
+ public static final String ARG_CASE = "android.arg.case";
+
+ public static final String CASE_NOMINATIVE = "android.nomative";
+ public static final String CASE_ACCUSATIVE = "android.accusative";
+ public static final String CASE_DATIVE = "android.dative";
+ public static final String CASE_ABLATIVE = "android.ablative";
+ public static final String CASE_GENITIVE = "android.genitive";
+ public static final String CASE_VOCATIVE = "android.vocative";
+ public static final String CASE_LOCATIVE = "android.locative";
+ public static final String CASE_INSTRUMENTAL = "android.instrumental";
+
+ public TtsSpan(String type, PersistableBundle args) {
+ mType = type;
+ mArgs = args;
+ }
+
+ public TtsSpan(Parcel src) {
+ mType = src.readString();
+ mArgs = src.readPersistableBundle();
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ public PersistableBundle getArgs() {
+ return mArgs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mType);
+ dest.writePersistableBundle(mArgs);
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return TextUtils.TTS_SPAN;
+ }
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index fa4564e..57d1beb 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -127,8 +127,6 @@
@Override
void destroyHardwareResources(View view) {
destroyResources(view);
- // mRootNode belongs to us and not a view, so we need to destroy it
- mRootNode.destroyDisplayListData();
nDestroyHardwareResources(mNativeProxy);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 706fb1c..1e5f448 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13625,6 +13625,10 @@
* @hide
*/
protected void destroyHardwareResources() {
+ // Although the Layer will be destroyed by RenderNode, we want to release
+ // the staging display list, which is also a signal to RenderNode that it's
+ // safe to free its copy of the display list as it knows that we will
+ // push an updated DisplayList if we try to draw again
resetDisplayList();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 5bd6f52..916586c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -689,7 +689,7 @@
try {
getLockSettings().setLockPassword(password, userHandle);
DevicePolicyManager dpm = getDevicePolicyManager();
- if (password != null) {
+ if (!TextUtils.isEmpty(password)) {
int computedQuality = computePasswordQuality(password);
if (userHandle == UserHandle.USER_OWNER) {
@@ -764,9 +764,10 @@
}
setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
} else {
+ // Empty password
if (userHandle == UserHandle.USER_OWNER) {
- // Update the encryption password.
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, password);
+ // Set the encryption password to default.
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
}
dpm.setActivePasswordState(
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index f7886d3..53aca3d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -316,7 +316,7 @@
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
jint offset, jint stride, jint width, jint height,
jint configHandle, jboolean isMutable) {
- SkColorType colorType = SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(configHandle));
+ SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
if (n < SkAbs32(stride) * (size_t)height) {
@@ -350,11 +350,11 @@
static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
jint dstConfigHandle, jboolean isMutable) {
const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
- SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
+ SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
SkBitmap result;
JavaPixelAllocator allocator(env);
- if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) {
+ if (!src->copyTo(&result, dstCT, &allocator)) {
return NULL;
}
return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
@@ -389,8 +389,7 @@
jint width, jint height, jint configHandle, jint allocSize,
jboolean requestPremul) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
- SkColorType colorType = SkBitmapConfigToColorType(config);
+ SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
// ARGB_4444 is a deprecated format, convert automatically to 8888
if (colorType == kARGB_4444_SkColorType) {
@@ -494,7 +493,7 @@
static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->config());
+ return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->colorType());
}
static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -810,7 +809,7 @@
const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
if (bm0->width() != bm1->width() ||
bm0->height() != bm1->height() ||
- bm0->config() != bm1->config()) {
+ bm0->colorType() != bm1->colorType()) {
return JNI_FALSE;
}
@@ -822,7 +821,7 @@
return JNI_FALSE;
}
- if (bm0->config() == SkBitmap::kIndex8_Config) {
+ if (bm0->colorType() == kIndex_8_SkColorType) {
SkColorTable* ct0 = bm0->getColorTable();
SkColorTable* ct1 = bm1->getColorTable();
if (NULL == ct0 || NULL == ct1) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 7a186a2..9177696 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -291,6 +291,54 @@
env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
}
+// This enum must keep these int values, to match the int values
+// in the java Bitmap.Config enum.
+enum LegacyBitmapConfig {
+ kNo_LegacyBitmapConfig = 0,
+ kA8_LegacyBitmapConfig = 1,
+ kIndex8_LegacyBitmapConfig = 2,
+ kRGB_565_LegacyBitmapConfig = 3,
+ kARGB_4444_LegacyBitmapConfig = 4,
+ kARGB_8888_LegacyBitmapConfig = 5,
+
+ kLastEnum_LegacyBitmapConfig = kARGB_8888_LegacyBitmapConfig
+};
+
+jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
+ switch (colorType) {
+ case kN32_SkColorType:
+ return kARGB_8888_LegacyBitmapConfig;
+ case kARGB_4444_SkColorType:
+ return kARGB_4444_LegacyBitmapConfig;
+ case kRGB_565_SkColorType:
+ return kRGB_565_LegacyBitmapConfig;
+ case kIndex_8_SkColorType:
+ return kIndex8_LegacyBitmapConfig;
+ case kAlpha_8_SkColorType:
+ return kA8_LegacyBitmapConfig;
+ case kUnknown_SkColorType:
+ default:
+ break;
+ }
+ return kNo_LegacyBitmapConfig;
+}
+
+SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
+ const uint8_t gConfig2ColorType[] = {
+ kUnknown_SkColorType,
+ kAlpha_8_SkColorType,
+ kIndex_8_SkColorType,
+ kRGB_565_SkColorType,
+ kARGB_4444_SkColorType,
+ kN32_SkColorType
+ };
+
+ if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) {
+ legacyConfig = kNo_LegacyBitmapConfig;
+ }
+ return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]);
+}
+
SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
SkASSERT(env);
SkASSERT(bitmap);
@@ -308,10 +356,7 @@
}
SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
- if (c < 0 || c >= SkBitmap::kConfigCount) {
- c = kUnknown_SkColorType;
- }
- return SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(c));
+ return legacyBitmapConfigToColorType(c);
}
SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 6d82ceb..a03391d 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -58,6 +58,14 @@
// ref to its SkRasterizer* (or NULL).
static SkRasterizer* refNativeRasterizer(jlong rasterizerHandle);
+ /*
+ * LegacyBitmapConfig is the old enum in Skia that matched the enum int values
+ * in Bitmap.Config. Skia no longer supports this config, but has replaced it
+ * with SkColorType. These routines convert between the two.
+ */
+ static SkColorType legacyBitmapConfigToColorType(jint legacyConfig);
+ static jint colorTypeToLegacyBitmapConfig(SkColorType colorType);
+
/** Return the corresponding native colorType from the java Config enum,
or kUnknown_SkColorType if the java object is null.
*/
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index 1793208..c162c48 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -38,18 +38,18 @@
#include <utils/Log.h>
static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
- switch (bitmap.config()) {
- case SkBitmap::kARGB_8888_Config:
+ switch (bitmap.colorType()) {
+ case kN32_SkColorType:
*c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
*c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
break;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
*c = SkUnPreMultiply::PMColorToColor(
SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
break;
- case SkBitmap::kIndex8_Config: {
+ case kIndex_8_SkColorType: {
SkColorTable* ctable = bitmap.getColorTable();
*c = SkUnPreMultiply::PMColorToColor(
(*ctable)[*bitmap.getAddr8(x, y)]);
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index a91c622..89baef8 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -562,18 +562,18 @@
setGLDebugLevel(level);
}
-static int checkFormat(SkBitmap::Config config, int format, int type)
+static int checkFormat(SkColorType colorType, int format, int type)
{
- switch(config) {
- case SkBitmap::kIndex8_Config:
+ switch(colorType) {
+ case kIndex_8_SkColorType:
if (format == GL_PALETTE8_RGBA8_OES)
return 0;
- case SkBitmap::kARGB_8888_Config:
- case SkBitmap::kA8_Config:
+ case kN32_SkColorType:
+ case kAlpha_8_SkColorType:
if (type == GL_UNSIGNED_BYTE)
return 0;
- case SkBitmap::kARGB_4444_Config:
- case SkBitmap::kRGB_565_Config:
+ case kARGB_4444_SkColorType:
+ case kRGB_565_SkColorType:
switch (type) {
case GL_UNSIGNED_SHORT_4_4_4_4:
case GL_UNSIGNED_SHORT_5_6_5:
@@ -590,36 +590,36 @@
return -1;
}
-static int getInternalFormat(SkBitmap::Config config)
+static int getInternalFormat(SkColorType colorType)
{
- switch(config) {
- case SkBitmap::kA8_Config:
+ switch(colorType) {
+ case kAlpha_8_SkColorType:
return GL_ALPHA;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
return GL_RGBA;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
return GL_RGBA;
- case SkBitmap::kIndex8_Config:
+ case kIndex_8_SkColorType:
return GL_PALETTE8_RGBA8_OES;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
return GL_RGB;
default:
return -1;
}
}
-static int getType(SkBitmap::Config config)
+static int getType(SkColorType colorType)
{
- switch(config) {
- case SkBitmap::kA8_Config:
+ switch(colorType) {
+ case kAlpha_8_SkColorType:
return GL_UNSIGNED_BYTE;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
return GL_UNSIGNED_SHORT_4_4_4_4;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
return GL_UNSIGNED_BYTE;
- case SkBitmap::kIndex8_Config:
+ case kIndex_8_SkColorType:
return -1; // No type for compressed data.
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
return GL_UNSIGNED_SHORT_5_6_5;
default:
return -1;
@@ -631,9 +631,7 @@
{
SkBitmap const * nativeBitmap =
(SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
- const SkBitmap& bitmap(*nativeBitmap);
- SkBitmap::Config config = bitmap.config();
- return getInternalFormat(config);
+ return getInternalFormat(nativeBitmap->colorType());
}
static jint util_getType(JNIEnv *env, jclass clazz,
@@ -641,9 +639,7 @@
{
SkBitmap const * nativeBitmap =
(SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
- const SkBitmap& bitmap(*nativeBitmap);
- SkBitmap::Config config = bitmap.config();
- return getType(config);
+ return getType(nativeBitmap->colorType());
}
static jint util_texImage2D(JNIEnv *env, jclass clazz,
@@ -653,14 +649,14 @@
SkBitmap const * nativeBitmap =
(SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
const SkBitmap& bitmap(*nativeBitmap);
- SkBitmap::Config config = bitmap.config();
+ SkColorType colorType = bitmap.colorType();
if (internalformat < 0) {
- internalformat = getInternalFormat(config);
+ internalformat = getInternalFormat(colorType);
}
if (type < 0) {
- type = getType(config);
+ type = getType(colorType);
}
- int err = checkFormat(config, internalformat, type);
+ int err = checkFormat(colorType, internalformat, type);
if (err)
return err;
bitmap.lockPixels();
@@ -702,13 +698,13 @@
SkBitmap const * nativeBitmap =
(SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
const SkBitmap& bitmap(*nativeBitmap);
- SkBitmap::Config config = bitmap.config();
+ SkColorType colorType = bitmap.colorType();
if (format < 0) {
- format = getInternalFormat(config);
+ format = getInternalFormat(colorType);
if (format == GL_PALETTE8_RGBA8_OES)
return -1; // glCompressedTexSubImage2D() not supported
}
- int err = checkFormat(config, format, type);
+ int err = checkFormat(colorType, format, type);
if (err)
return err;
bitmap.lockPixels();
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 69e991d..c9a0b1e 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -37,6 +37,11 @@
jmethodID add;
} gArrayListMethods;
+static jclass gUUIDClass;
+static struct {
+ jmethodID toString;
+} gUUIDMethods;
+
static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
static jclass gSoundTriggerClass;
@@ -57,41 +62,65 @@
"android/hardware/soundtrigger/SoundTrigger$SoundModel";
static jclass gSoundModelClass;
static struct {
+ jfieldID uuid;
jfieldID data;
} gSoundModelFields;
-static const char* const kKeyPhraseClassPathName =
- "android/hardware/soundtrigger/SoundTrigger$KeyPhrase";
-static jclass gKeyPhraseClass;
+static const char* const kKeyphraseClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$Keyphrase";
+static jclass gKeyphraseClass;
static struct {
+ jfieldID id;
jfieldID recognitionModes;
jfieldID locale;
jfieldID text;
- jfieldID numUsers;
-} gKeyPhraseFields;
+ jfieldID users;
+} gKeyphraseFields;
-static const char* const kKeyPhraseSoundModelClassPathName =
- "android/hardware/soundtrigger/SoundTrigger$KeyPhraseSoundModel";
-static jclass gKeyPhraseSoundModelClass;
+static const char* const kKeyphraseSoundModelClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel";
+static jclass gKeyphraseSoundModelClass;
static struct {
- jfieldID keyPhrases;
-} gKeyPhraseSoundModelFields;
+ jfieldID keyphrases;
+} gKeyphraseSoundModelFields;
+static const char* const kRecognitionConfigClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig";
+static jclass gRecognitionConfigClass;
+static struct {
+ jfieldID captureRequested;
+ jfieldID keyphrases;
+ jfieldID data;
+} gRecognitionConfigFields;
static const char* const kRecognitionEventClassPathName =
"android/hardware/soundtrigger/SoundTrigger$RecognitionEvent";
static jclass gRecognitionEventClass;
static jmethodID gRecognitionEventCstor;
-static const char* const kKeyPhraseRecognitionEventClassPathName =
- "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionEvent";
-static jclass gKeyPhraseRecognitionEventClass;
-static jmethodID gKeyPhraseRecognitionEventCstor;
+static const char* const kKeyphraseRecognitionEventClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent";
+static jclass gKeyphraseRecognitionEventClass;
+static jmethodID gKeyphraseRecognitionEventCstor;
-static const char* const kKeyPhraseRecognitionExtraClassPathName =
- "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra";
-static jclass gKeyPhraseRecognitionExtraClass;
-static jmethodID gKeyPhraseRecognitionExtraCstor;
+static const char* const kKeyphraseRecognitionExtraClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
+static jclass gKeyphraseRecognitionExtraClass;
+static jmethodID gKeyphraseRecognitionExtraCstor;
+static struct {
+ jfieldID id;
+ jfieldID recognitionModes;
+ jfieldID confidenceLevels;
+} gKeyphraseRecognitionExtraFields;
+
+static const char* const kConfidenceLevelClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel";
+static jclass gConfidenceLevelClass;
+static jmethodID gConfidenceLevelCstor;
+static struct {
+ jfieldID userId;
+ jfieldID confidenceLevel;
+} gConfidenceLevelFields;
static Mutex gLock;
@@ -170,40 +199,51 @@
(struct sound_trigger_phrase_recognition_event *)event;
jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases,
- gKeyPhraseRecognitionExtraClass, NULL);
+ gKeyphraseRecognitionExtraClass, NULL);
if (jExtras == NULL) {
return;
}
for (size_t i = 0; i < phraseEvent->num_phrases; i++) {
- jintArray jConfidenceLevels = env->NewIntArray(phraseEvent->phrase_extras[i].num_users);
+ jobjectArray jConfidenceLevels = env->NewObjectArray(
+ phraseEvent->phrase_extras[i].num_levels,
+ gConfidenceLevelClass, NULL);
+
if (jConfidenceLevels == NULL) {
return;
}
- jint *nConfidenceLevels = env->GetIntArrayElements(jConfidenceLevels, NULL);
- memcpy(nConfidenceLevels,
- phraseEvent->phrase_extras[i].confidence_levels,
- phraseEvent->phrase_extras[i].num_users * sizeof(int));
- env->ReleaseIntArrayElements(jConfidenceLevels, nConfidenceLevels, 0);
- jobject jNewExtra = env->NewObject(gKeyPhraseRecognitionExtraClass,
- gKeyPhraseRecognitionExtraCstor,
- jConfidenceLevels,
- phraseEvent->phrase_extras[i].recognition_modes);
+ for (size_t j = 0; j < phraseEvent->phrase_extras[i].num_levels; j++) {
+ jobject jConfidenceLevel = env->NewObject(gConfidenceLevelClass,
+ gConfidenceLevelCstor,
+ phraseEvent->phrase_extras[i].levels[j].user_id,
+ phraseEvent->phrase_extras[i].levels[j].level);
+ env->SetObjectArrayElement(jConfidenceLevels, j, jConfidenceLevel);
+ env->DeleteLocalRef(jConfidenceLevel);
+ }
+
+ jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass,
+ gKeyphraseRecognitionExtraCstor,
+ phraseEvent->phrase_extras[i].id,
+ phraseEvent->phrase_extras[i].recognition_modes,
+ jConfidenceLevels);
if (jNewExtra == NULL) {
return;
}
env->SetObjectArrayElement(jExtras, i, jNewExtra);
-
+ env->DeleteLocalRef(jNewExtra);
+ env->DeleteLocalRef(jConfidenceLevels);
}
- jEvent = env->NewObject(gKeyPhraseRecognitionEventClass, gKeyPhraseRecognitionEventCstor,
+ jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor,
event->status, event->model, event->capture_available,
- event->capture_session, event->capture_delay_ms, jData,
+ event->capture_session, event->capture_delay_ms,
+ event->capture_preamble_ms, jData,
phraseEvent->key_phrase_in_capture, jExtras);
} else {
jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor,
event->status, event->model, event->capture_available,
- event->capture_session, event->capture_delay_ms, jData);
+ event->capture_session, event->capture_delay_ms,
+ event->capture_preamble_ms, jData);
}
@@ -374,13 +414,16 @@
jobject jSoundModel, jintArray jHandle)
{
jint status = SOUNDTRIGGER_STATUS_OK;
- char *nData = NULL;
+ jbyte *nData = NULL;
struct sound_trigger_sound_model *nSoundModel;
jbyteArray jData;
sp<MemoryDealer> memoryDealer;
sp<IMemory> memory;
size_t size;
sound_model_handle_t handle;
+ jobject jUuid;
+ jstring jUuidString;
+ const char *nUuidString;
ALOGV("loadSoundModel");
sp<SoundTrigger> module = getSoundTrigger(env, thiz);
@@ -404,13 +447,22 @@
}
size_t offset;
sound_trigger_sound_model_type_t type;
- if (env->IsInstanceOf(jSoundModel, gKeyPhraseSoundModelClass)) {
+ if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) {
offset = sizeof(struct sound_trigger_phrase_sound_model);
type = SOUND_MODEL_TYPE_KEYPHRASE;
} else {
offset = sizeof(struct sound_trigger_sound_model);
type = SOUND_MODEL_TYPE_UNKNOWN;
}
+
+ jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid);
+ jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString);
+ nUuidString = env->GetStringUTFChars(jUuidString, NULL);
+ sound_trigger_uuid_t nUuid;
+ SoundTrigger::stringToGuid(nUuidString, &nUuid);
+ env->ReleaseStringUTFChars(jUuidString, nUuidString);
+ env->DeleteLocalRef(jUuidString);
+
jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data);
if (jData == NULL) {
status = SOUNDTRIGGER_STATUS_BAD_VALUE;
@@ -418,7 +470,7 @@
}
size = env->GetArrayLength(jData);
- nData = (char *)env->GetByteArrayElements(jData, NULL);
+ nData = env->GetByteArrayElements(jData, NULL);
if (jData == NULL) {
status = SOUNDTRIGGER_STATUS_ERROR;
goto exit;
@@ -438,6 +490,7 @@
nSoundModel = (struct sound_trigger_sound_model *)memory->pointer();
nSoundModel->type = type;
+ nSoundModel->uuid = nUuid;
nSoundModel->data_size = size;
nSoundModel->data_offset = offset;
memcpy((char *)nSoundModel + offset, nData, size);
@@ -446,7 +499,7 @@
(struct sound_trigger_phrase_sound_model *)nSoundModel;
jobjectArray jPhrases =
- (jobjectArray)env->GetObjectField(jSoundModel, gKeyPhraseSoundModelFields.keyPhrases);
+ (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases);
if (jPhrases == NULL) {
status = SOUNDTRIGGER_STATUS_BAD_VALUE;
goto exit;
@@ -457,16 +510,26 @@
ALOGV("loadSoundModel numPhrases %d", numPhrases);
for (size_t i = 0; i < numPhrases; i++) {
jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
+ phraseModel->phrases[i].id =
+ env->GetIntField(jPhrase,gKeyphraseFields.id);
phraseModel->phrases[i].recognition_mode =
- env->GetIntField(jPhrase,gKeyPhraseFields.recognitionModes);
- phraseModel->phrases[i].num_users =
- env->GetIntField(jPhrase, gKeyPhraseFields.numUsers);
- jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.locale);
+ env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes);
+
+ jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users);
+ phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers);
+ jint *nUsers = env->GetIntArrayElements(jUsers, NULL);
+ memcpy(phraseModel->phrases[i].users,
+ nUsers,
+ phraseModel->phrases[i].num_users * sizeof(int));
+ env->ReleaseIntArrayElements(jUsers, nUsers, 0);
+ env->DeleteLocalRef(jUsers);
+
+ jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale);
const char *nLocale = env->GetStringUTFChars(jLocale, NULL);
strncpy(phraseModel->phrases[i].locale,
nLocale,
SOUND_TRIGGER_MAX_LOCALE_LEN);
- jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.text);
+ jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text);
const char *nText = env->GetStringUTFChars(jText, NULL);
strncpy(phraseModel->phrases[i].text,
nText,
@@ -478,6 +541,7 @@
env->DeleteLocalRef(jText);
ALOGV("loadSoundModel phrases %d text %s locale %s",
i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale);
+ env->DeleteLocalRef(jPhrase);
}
env->DeleteLocalRef(jPhrases);
}
@@ -490,7 +554,7 @@
env->ReleaseIntArrayElements(jHandle, nHandle, NULL);
}
if (nData != NULL) {
- env->ReleaseByteArrayElements(jData, (jbyte *)nData, NULL);
+ env->ReleaseByteArrayElements(jData, nData, NULL);
}
return status;
}
@@ -512,7 +576,7 @@
static jint
android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
- jint jHandle, jbyteArray jData)
+ jint jHandle, jobject jConfig)
{
jint status = SOUNDTRIGGER_STATUS_OK;
ALOGV("startRecognition");
@@ -520,30 +584,83 @@
if (module == NULL) {
return SOUNDTRIGGER_STATUS_ERROR;
}
+
+ if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) {
+ return SOUNDTRIGGER_STATUS_BAD_VALUE;
+ }
+
+ jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data);
jsize dataSize = 0;
- char *nData = NULL;
- sp<IMemory> memory;
+ jbyte *nData = NULL;
if (jData != NULL) {
dataSize = env->GetArrayLength(jData);
if (dataSize == 0) {
return SOUNDTRIGGER_STATUS_BAD_VALUE;
}
- nData = (char *)env->GetByteArrayElements(jData, NULL);
+ nData = env->GetByteArrayElements(jData, NULL);
if (nData == NULL) {
return SOUNDTRIGGER_STATUS_ERROR;
}
- sp<MemoryDealer> memoryDealer =
- new MemoryDealer(dataSize, "SoundTrigge-JNI::StartRecognition");
- if (memoryDealer == 0) {
- return SOUNDTRIGGER_STATUS_ERROR;
- }
- memory = memoryDealer->allocate(dataSize);
- if (memory == 0 || memory->pointer() == NULL) {
- return SOUNDTRIGGER_STATUS_ERROR;
- }
- memcpy(memory->pointer(), nData, dataSize);
}
+ size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize;
+ sp<MemoryDealer> memoryDealer =
+ new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition");
+ if (memoryDealer == 0) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ sp<IMemory> memory = memoryDealer->allocate(totalSize);
+ if (memory == 0 || memory->pointer() == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ if (dataSize != 0) {
+ memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config),
+ nData,
+ dataSize);
+ env->ReleaseByteArrayElements(jData, nData, 0);
+ }
+ env->DeleteLocalRef(jData);
+ struct sound_trigger_recognition_config *config =
+ (struct sound_trigger_recognition_config *)memory->pointer();
+ config->data_size = dataSize;
+ config->data_offset = sizeof(struct sound_trigger_recognition_config);
+ config->capture_requested = env->GetIntField(jConfig,
+ gRecognitionConfigFields.captureRequested);
+
+ config->num_phrases = 0;
+ jobjectArray jPhrases =
+ (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases);
+ if (jPhrases != NULL) {
+ config->num_phrases = env->GetArrayLength(jPhrases);
+ }
+ ALOGV("startRecognition num phrases %d", config->num_phrases);
+ for (size_t i = 0; i < config->num_phrases; i++) {
+ jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
+ config->phrases[i].id = env->GetIntField(jPhrase,
+ gKeyphraseRecognitionExtraFields.id);
+ config->phrases[i].recognition_modes = env->GetIntField(jPhrase,
+ gKeyphraseRecognitionExtraFields.recognitionModes);
+ config->phrases[i].num_levels = 0;
+ jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase,
+ gKeyphraseRecognitionExtraFields.confidenceLevels);
+ if (jConfidenceLevels != NULL) {
+ config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels);
+ }
+ ALOGV("startRecognition phrase %d num_levels %d", i, config->phrases[i].num_levels);
+ for (size_t j = 0; j < config->phrases[i].num_levels; j++) {
+ jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j);
+ config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel,
+ gConfidenceLevelFields.userId);
+ config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel,
+ gConfidenceLevelFields.confidenceLevel);
+ env->DeleteLocalRef(jConfidenceLevel);
+ }
+ ALOGV("startRecognition phrases %d", i);
+ env->DeleteLocalRef(jConfidenceLevels);
+ env->DeleteLocalRef(jPhrase);
+ }
+ env->DeleteLocalRef(jPhrases);
+
status = module->startRecognition(jHandle, memory);
return status;
}
@@ -586,7 +703,7 @@
"(I)I",
(void *)android_hardware_SoundTrigger_unloadSoundModel},
{"startRecognition",
- "(I[B)I",
+ "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I",
(void *)android_hardware_SoundTrigger_startRecognition},
{"stopRecognition",
"(I)I",
@@ -599,6 +716,10 @@
gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+ jclass uuidClass = env->FindClass("java/util/UUID");
+ gUUIDClass = (jclass) env->NewGlobalRef(uuidClass);
+ gUUIDMethods.toString = env->GetMethodID(uuidClass, "toString", "()Ljava/lang/String;");
+
jclass lClass = env->FindClass(kSoundTriggerClassPathName);
gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass);
@@ -617,37 +738,63 @@
jclass soundModelClass = env->FindClass(kSoundModelClassPathName);
gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass);
+ gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;");
gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B");
- jclass keyPhraseClass = env->FindClass(kKeyPhraseClassPathName);
- gKeyPhraseClass = (jclass) env->NewGlobalRef(keyPhraseClass);
- gKeyPhraseFields.recognitionModes = env->GetFieldID(keyPhraseClass, "recognitionModes", "I");
- gKeyPhraseFields.locale = env->GetFieldID(keyPhraseClass, "locale", "Ljava/lang/String;");
- gKeyPhraseFields.text = env->GetFieldID(keyPhraseClass, "text", "Ljava/lang/String;");
- gKeyPhraseFields.numUsers = env->GetFieldID(keyPhraseClass, "numUsers", "I");
+ jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName);
+ gKeyphraseClass = (jclass) env->NewGlobalRef(keyphraseClass);
+ gKeyphraseFields.id = env->GetFieldID(keyphraseClass, "id", "I");
+ gKeyphraseFields.recognitionModes = env->GetFieldID(keyphraseClass, "recognitionModes", "I");
+ gKeyphraseFields.locale = env->GetFieldID(keyphraseClass, "locale", "Ljava/lang/String;");
+ gKeyphraseFields.text = env->GetFieldID(keyphraseClass, "text", "Ljava/lang/String;");
+ gKeyphraseFields.users = env->GetFieldID(keyphraseClass, "users", "[I");
- jclass keyPhraseSoundModelClass = env->FindClass(kKeyPhraseSoundModelClassPathName);
- gKeyPhraseSoundModelClass = (jclass) env->NewGlobalRef(keyPhraseSoundModelClass);
- gKeyPhraseSoundModelFields.keyPhrases = env->GetFieldID(keyPhraseSoundModelClass,
- "keyPhrases",
- "[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhrase;");
+ jclass keyphraseSoundModelClass = env->FindClass(kKeyphraseSoundModelClassPathName);
+ gKeyphraseSoundModelClass = (jclass) env->NewGlobalRef(keyphraseSoundModelClass);
+ gKeyphraseSoundModelFields.keyphrases = env->GetFieldID(keyphraseSoundModelClass,
+ "keyphrases",
+ "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;");
jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName);
gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass);
gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>",
- "(IIZII[B)V");
+ "(IIZIII[B)V");
- jclass keyPhraseRecognitionEventClass = env->FindClass(kKeyPhraseRecognitionEventClassPathName);
- gKeyPhraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionEventClass);
- gKeyPhraseRecognitionEventCstor = env->GetMethodID(keyPhraseRecognitionEventClass, "<init>",
- "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra;)V");
+ jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName);
+ gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass);
+ gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>",
+ "(IIZIII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
- jclass keyPhraseRecognitionExtraClass = env->FindClass(kKeyPhraseRecognitionExtraClassPathName);
- gKeyPhraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionExtraClass);
- gKeyPhraseRecognitionExtraCstor = env->GetMethodID(keyPhraseRecognitionExtraClass, "<init>",
- "([II)V");
+ jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName);
+ gRecognitionConfigClass = (jclass) env->NewGlobalRef(keyRecognitionConfigClass);
+ gRecognitionConfigFields.captureRequested = env->GetFieldID(keyRecognitionConfigClass,
+ "captureRequested",
+ "Z");
+ gRecognitionConfigFields.keyphrases = env->GetFieldID(keyRecognitionConfigClass,
+ "keyphrases",
+ "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;");
+ gRecognitionConfigFields.data = env->GetFieldID(keyRecognitionConfigClass,
+ "data",
+ "[B");
+
+ jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName);
+ gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass);
+ gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>",
+ "(II[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V");
+ gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I");
+ gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, "recognitionModes", "I");
+ gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass,
+ "confidenceLevels",
+ "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;");
+
+ jclass confidenceLevelClass = env->FindClass(kConfidenceLevelClassPathName);
+ gConfidenceLevelClass = (jclass) env->NewGlobalRef(confidenceLevelClass);
+ gConfidenceLevelCstor = env->GetMethodID(confidenceLevelClass, "<init>", "(II)V");
+ gConfidenceLevelFields.userId = env->GetFieldID(confidenceLevelClass, "userId", "I");
+ gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass,
+ "confidenceLevel", "I");
int status = AndroidRuntime::registerNativeMethods(env,
kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
@@ -657,5 +804,6 @@
kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
}
+
return status;
}
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 3d421d5..8ad2eea 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -257,13 +257,13 @@
return reinterpret_cast<jlong>(sur);
}
-static PixelFormat convertPixelFormat(SkBitmap::Config format)
+static PixelFormat convertPixelFormat(SkColorType format)
{
switch (format) {
- case SkBitmap::kARGB_8888_Config: return PIXEL_FORMAT_RGBA_8888;
- case SkBitmap::kARGB_4444_Config: return PIXEL_FORMAT_RGBA_4444;
- case SkBitmap::kRGB_565_Config: return PIXEL_FORMAT_RGB_565;
- default: return PIXEL_FORMAT_NONE;
+ case kN32_SkColorType: return PIXEL_FORMAT_RGBA_8888;
+ case kARGB_4444_SkColorType: return PIXEL_FORMAT_RGBA_4444;
+ case kRGB_565_SkColorType: return PIXEL_FORMAT_RGB_565;
+ default: return PIXEL_FORMAT_NONE;
}
}
@@ -297,7 +297,7 @@
pixmap.width = nativeBitmap->width();
pixmap.height = nativeBitmap->height();
pixmap.stride = nativeBitmap->rowBytes() / nativeBitmap->bytesPerPixel();
- pixmap.format = convertPixelFormat(nativeBitmap->config());
+ pixmap.format = convertPixelFormat(nativeBitmap->colorType());
pixmap.data = (uint8_t*)ref->pixels();
base = beginNativeAttribList(_env, attrib_list);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e675dc7..2d1cc59 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1068,14 +1068,6 @@
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature" />
- <!-- Allows an application to communicate with a SIM card using logical
- channels. -->
- <permission android:name="android.permission.SIM_COMMUNICATION"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:label="@string/permlab_sim_communication"
- android:description="@string/permdesc_sim_communication"
- android:protectionLevel="dangerous" />
-
<!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
built-in tuners and HDMI-in's.
@hide This should only be used by OEM's TvInputService's.
diff --git a/core/res/res/values-mcc214-mnc07/config.xml b/core/res/res/values-mcc214-mnc07/config.xml
new file mode 100644
index 0000000..ce7526c
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc07/config.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
+ <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+ <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+ <integer-array translatable="false" name="config_tether_upstream_types">
+ <item>1</item>
+ <item>4</item>
+ <item>7</item>
+ <item>9</item>
+ </integer-array>
+
+ <!-- String containing the apn value for tethering. May be overriden by secure settings
+ TETHER_DUN_APN. Value is a comma separated series of strings:
+ "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+ note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
+ <string translatable="false" name="config_tether_apndata">Conexion Compartida,movistar.es,,,MOVISTAR,MOVISTAR,,,,,214,07,1,DUN</string>
+
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7f8fe4d..f34fbc13 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -31,6 +31,7 @@
<item><xliff:g id="id">nfc</xliff:g></item>
<item><xliff:g id="id">tty</xliff:g></item>
<item><xliff:g id="id">speakerphone</xliff:g></item>
+ <item><xliff:g id="id">zen</xliff:g></item>
<item><xliff:g id="id">mute</xliff:g></item>
<item><xliff:g id="id">volume</xliff:g></item>
<item><xliff:g id="id">wifi</xliff:g></item>
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 2e99194..16bc444 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -781,7 +781,7 @@
$ rm /tmp/device_owner.xml
$ echo "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
>> /tmp/device_owner.xml
-$ echo "&device-owner package=\"<your_device_owner_package>\"
+$ echo "<device-owner package=\"<your_device_owner_package>\"
name=\"*<your_organization_name>\" />" >> /tmp/device_owner.xml
$ adb push /tmp/device_owner.xml /data/system/device_owner.xml
$ adb reboot
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index e905285..68eca49 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -61,6 +61,33 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+/>Android Support Library, revision 20</a> <em>(July 2014)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+ <dl>
+ <dt>Changes for v4 support library:</dt>
+ <dd>
+ <ul>
+ <li>Added extended notification support for Android Wear in
+ {@link android.support.v4.app.NotificationCompat.WearableExtender}, which allows you
+ to specify wearable-specific features in your notifications.</li>
+ <li>Added {@link android.support.v4.app.NotificationCompat.Action.WearableExtender},
+ which allows actions to be added on wearable notifications.</li>
+ <li>Added {@link android.support.v4.app.NotificationManagerCompat}, which allows you
+ to issue notifications that properly support wearable features.</li>
+ <li>Added {@link android.support.v4.app.RemoteInput}, which allows a handheld device
+ to receive voice input from a notification that appears on a wearable device.</li>
+ <li>Improved the handling of touch feedback in
+ {@link android.support.v4.widget.SwipeRefreshLayout}.</li>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
/>Android Support Library, revision 19.1.0</a> <em>(March 2014)</em>
</p>
<div class="toggle-content-toggleme">
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 901c69e..75d52b4 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -732,7 +732,7 @@
deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
!state.mClipSideFlags &&
OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
- (mBitmap->config() != SkBitmap::kA8_Config);
+ (mBitmap->colorType() != kAlpha_8_SkColorType);
}
const SkBitmap* bitmap() { return mBitmap; }
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 873baf5..a92ef94 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -383,20 +383,20 @@
GLenum error = GL_NO_ERROR;
bool status = false;
- switch (bitmap->config()) {
- case SkBitmap::kA8_Config:
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
format = GL_ALPHA;
type = GL_UNSIGNED_BYTE;
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
break;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
default:
format = GL_RGBA;
type = GL_UNSIGNED_BYTE;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1bbcff1..c9f541b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1957,7 +1957,7 @@
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
- if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, paint, &vertices[0].x, &vertices[0].u,
GL_TRIANGLES, bitmapCount * 6, true,
@@ -1986,7 +1986,7 @@
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
- if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlphaBitmap(texture, left, top, paint);
} else {
drawTextureRect(left, top, right, bottom, texture, paint);
@@ -2014,7 +2014,7 @@
// to the vertex shader. The save/restore is a bit overkill.
save(SkCanvas::kMatrix_SaveFlag);
concatMatrix(matrix);
- if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlphaBitmap(texture, 0.0f, 0.0f, paint);
} else {
drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
@@ -2037,7 +2037,7 @@
Texture* texture = mCaches.textureCache.getTransient(bitmap);
const AutoTexture autoCleanup(texture);
- if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlphaBitmap(texture, left, top, paint);
} else {
drawTextureRect(left, top, right, bottom, texture, paint);
@@ -2232,7 +2232,7 @@
dstBottom = srcBottom - srcTop;
}
- if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+ if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
texture->id, paint,
&mMeshVertices[0].x, &mMeshVertices[0].u,
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 3cf625f..fe03806 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -63,11 +63,12 @@
, mDisplayListData(0)
, mStagingDisplayListData(0)
, mAnimatorManager(*this)
- , mLayer(0) {
+ , mLayer(0)
+ , mParentCount(0) {
}
RenderNode::~RenderNode() {
- delete mDisplayListData;
+ deleteDisplayListData();
delete mStagingDisplayListData;
LayerRenderer::destroyLayerDeferred(mLayer);
}
@@ -196,28 +197,12 @@
void RenderNode::prepareTreeImpl(TreeInfo& info) {
info.damageAccumulator->pushTransform(this);
- switch (info.mode) {
- case TreeInfo::MODE_FULL:
+ if (info.mode == TreeInfo::MODE_FULL) {
pushStagingPropertiesChanges(info);
- mAnimatorManager.animate(info);
- break;
- case TreeInfo::MODE_MAYBE_DETACHING:
- pushStagingPropertiesChanges(info);
- break;
- case TreeInfo::MODE_RT_ONLY:
- mAnimatorManager.animate(info);
- break;
- case TreeInfo::MODE_DESTROY_RESOURCES:
- // This will also release the hardware layer if we have one as
- // isRenderable() will return false, thus causing pushLayerUpdate
- // to recycle the hardware layer
- LOG_ALWAYS_FATAL_IF(mStagingDisplayListData || (mDisplayListData && !mNeedsDisplayListDataSync),
- "View.destroyHardwareResources wasn't called!");
- break;
}
-
+ mAnimatorManager.animate(info);
prepareLayer(info);
- if (info.mode == TreeInfo::MODE_FULL || info.mode == TreeInfo::MODE_DESTROY_RESOURCES) {
+ if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info);
}
prepareSubTree(info, mDisplayListData);
@@ -259,21 +244,30 @@
void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
if (mNeedsDisplayListDataSync) {
mNeedsDisplayListDataSync = false;
- // Do a push pass on the old tree to handle freeing DisplayListData
- // that are no longer used
- TreeInfo::TraversalMode mode = TreeInfo::MODE_MAYBE_DETACHING;
- if (CC_UNLIKELY(info.mode == TreeInfo::MODE_DESTROY_RESOURCES)) {
- mode = TreeInfo::MODE_DESTROY_RESOURCES;
+ // Make sure we inc first so that we don't fluctuate between 0 and 1,
+ // which would thrash the layer cache
+ if (mStagingDisplayListData) {
+ for (size_t i = 0; i < mStagingDisplayListData->children().size(); i++) {
+ mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount();
+ }
}
- TreeInfo oldTreeInfo(mode, info);
- prepareSubTree(oldTreeInfo, mDisplayListData);
- delete mDisplayListData;
+ deleteDisplayListData();
mDisplayListData = mStagingDisplayListData;
- mStagingDisplayListData = 0;
+ mStagingDisplayListData = NULL;
damageSelf(info);
}
}
+void RenderNode::deleteDisplayListData() {
+ if (mDisplayListData) {
+ for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+ mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
+ }
+ }
+ delete mDisplayListData;
+ mDisplayListData = NULL;
+}
+
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
@@ -296,6 +290,35 @@
}
}
+void RenderNode::destroyHardwareResources() {
+ if (mLayer) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = NULL;
+ }
+ if (mDisplayListData) {
+ for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+ mDisplayListData->children()[i]->mRenderNode->destroyHardwareResources();
+ }
+ if (mNeedsDisplayListDataSync) {
+ // Next prepare tree we are going to push a new display list, so we can
+ // drop our current one now
+ deleteDisplayListData();
+ }
+ }
+}
+
+void RenderNode::decParentRefCount() {
+ LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
+ mParentCount--;
+ if (!mParentCount) {
+ // If a child of ours is being attached to our parent then this will incorrectly
+ // destroy its hardware resources. However, this situation is highly unlikely
+ // and the failure is "just" that the layer is re-created, so this should
+ // be safe enough
+ destroyHardwareResources();
+ }
+}
+
/*
* For property operations, we pass a savecount of 0, since the operations aren't part of the
* displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 7d42b59..54fa143 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -168,6 +168,7 @@
}
ANDROID_API virtual void prepareTree(TreeInfo& info);
+ void destroyHardwareResources();
// UI thread only!
ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
@@ -248,6 +249,10 @@
void applyLayerPropertiesToLayer(TreeInfo& info);
void prepareLayer(TreeInfo& info);
void pushLayerUpdate(TreeInfo& info);
+ void deleteDisplayListData();
+
+ void incParentRefCount() { mParentCount++; }
+ void decParentRefCount();
String8 mName;
@@ -256,6 +261,7 @@
RenderProperties mStagingProperties;
bool mNeedsDisplayListDataSync;
+ // WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
DisplayListData* mDisplayListData;
DisplayListData* mStagingDisplayListData;
@@ -272,6 +278,14 @@
// for projection surfaces, contains a list of all children items
Vector<DrawRenderNodeOp*> mProjectedNodes;
+
+ // How many references our parent(s) have to us. Typically this should alternate
+ // between 2 and 1 (when a staging push happens we inc first then dec)
+ // When this hits 0 we are no longer in the tree, so any hardware resources
+ // (specifically Layers) should be released.
+ // This is *NOT* thread-safe, and should therefore only be tracking
+ // mDisplayListData, not mStagingDisplayListData.
+ uint32_t mParentCount;
}; // class RenderNode
} /* namespace uirenderer */
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 9212d0a..ec9e30a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -285,20 +285,20 @@
Caches::getInstance().bindTexture(texture->id);
- switch (bitmap->config()) {
- case SkBitmap::kA8_Config:
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
- case SkBitmap::kARGB_8888_Config:
+ case kN32_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
@@ -306,14 +306,14 @@
// decoding happened
texture->blend = !bitmap->isOpaque();
break;
- case SkBitmap::kARGB_4444_Config:
- case SkBitmap::kIndex8_Config:
+ case kARGB_4444_SkColorType:
+ case kIndex_8_SkColorType:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
texture->blend = !bitmap->isOpaque();
break;
default:
- ALOGW("Unsupported bitmap config: %d", bitmap->config());
+ ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
break;
}
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 9746ac53..de09755 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -58,15 +58,6 @@
// animators, but potentially things like SurfaceTexture updates
// could be handled by this as well if there are no listeners
MODE_RT_ONLY,
- // The subtree is being detached. Maybe. If the RenderNode is present
- // in both the old and new display list's children then it will get a
- // MODE_MAYBE_DETACHING followed shortly by a MODE_FULL.
- // Push any pending display list changes in case it is detached,
- // but don't evaluate animators and such as if it isn't detached as a
- // MODE_FULL will follow shortly.
- MODE_MAYBE_DETACHING,
- // Destroy all hardware resources, including DisplayListData, in the tree.
- MODE_DESTROY_RESOURCES,
};
explicit TreeInfo(TraversalMode mode, RenderState& renderState)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f5d4f8b..57279b7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -250,8 +250,7 @@
stopDrawing();
if (mEglManager.hasEglContext()) {
requireGlContext();
- TreeInfo info(TreeInfo::MODE_DESTROY_RESOURCES, mRenderThread.renderState());
- mRootRenderNode->prepareTree(info);
+ mRootRenderNode->destroyHardwareResources();
Caches::getInstance().flush(Caches::kFlushMode_Layers);
}
}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 011da35..2854007 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.media.tv.ITvInputSession;
+import android.net.Uri;
import android.os.Bundle;
import android.view.InputChannel;
@@ -34,4 +35,5 @@
void onVideoStreamChanged(int width, int height, boolean interlaced, int seq);
void onAudioStreamChanged(int channelCount, int seq);
void onClosedCaptionStreamChanged(boolean hasClosedCaption, int seq);
+ void onChannelRetuned(in Uri channelUri, int seq);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 00f2922a..5a57ccd 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.media.tv.ITvInputSession;
+import android.net.Uri;
import android.os.Bundle;
/**
@@ -30,4 +31,5 @@
void onVideoStreamChanged(int width, int height, boolean interlaced);
void onAudioStreamChanged(int channelCount);
void onClosedCaptionStreamChanged(boolean hasClosedCaption);
+ void onChannelRetuned(in Uri channelUri);
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index daa7009..834ce64 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -125,6 +125,16 @@
}
/**
+ * This is called when the channel of this session is changed by the underlying TV input
+ * with out any {@link TvInputManager.Session#tune(Uri)} request.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param channelUri The URI of a channel.
+ */
+ public void onChannelRetuned(Session session, Uri channelUri) {
+ }
+
+ /**
* This is called when a custom event has been sent from this session.
*
* @param session A {@link TvInputManager.Session} associated with this callback
@@ -194,6 +204,15 @@
});
}
+ public void postChannelRetuned(final Uri channelUri) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onChannelRetuned(mSession, channelUri);
+ }
+ });
+ }
+
public void postSessionEvent(final String eventType, final Bundle eventArgs) {
mHandler.post(new Runnable() {
@Override
@@ -318,6 +337,18 @@
}
@Override
+ public void onChannelRetuned(Uri channelUri, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postChannelRetuned(channelUri);
+ }
+ }
+
+ @Override
public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 0f4a930..1e512cd 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -286,6 +286,25 @@
}
/**
+ * Notifies the channel of the session is retuned by TV input.
+ *
+ * @param channelUri The URI of a channel.
+ */
+ public void dispatchChannelRetuned(final Uri channelUri) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchChannelRetuned");
+ mSessionCallback.onChannelRetuned(channelUri);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in dispatchChannelRetuned");
+ }
+ }
+ });
+ }
+
+ /**
* Called when the session is released.
*/
public abstract void onRelease();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index a913e59c..664a215 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -456,6 +456,16 @@
}
/**
+ * This is invoked when the channel of this TvView is changed by the underlying TV input
+ * with out any {@link TvView#tune(String, Uri)} request.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param channelUri The URI of a channel.
+ */
+ public void onChannelRetuned(String inputId, Uri channelUri) {
+ }
+
+ /**
* This is invoked when a custom event from the bound TV input is sent to this view.
*
* @param eventType The type of the event.
@@ -562,6 +572,16 @@
}
@Override
+ public void onChannelRetuned(Session session, Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "onChannelChangedByTvInput(" + channelUri + ")");
+ }
+ if (mListener != null) {
+ mListener.onChannelRetuned(mInputId, channelUri);
+ }
+ }
+
+ @Override
public void onSessionEvent(TvInputManager.Session session, String eventType,
Bundle eventArgs) {
if (mListener != null) {
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index eaa2cbe..df0751d 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -34,17 +34,17 @@
info->stride = bm->rowBytes();
info->flags = 0;
- switch (bm->config()) {
- case SkBitmap::kARGB_8888_Config:
+ switch (bm->colorType()) {
+ case kN32_SkColorType:
info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
break;
- case SkBitmap::kRGB_565_Config:
+ case kRGB_565_SkColorType:
info->format = ANDROID_BITMAP_FORMAT_RGB_565;
break;
- case SkBitmap::kARGB_4444_Config:
+ case kARGB_4444_SkColorType:
info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
break;
- case SkBitmap::kA8_Config:
+ case kAlpha_8_SkColorType:
info->format = ANDROID_BITMAP_FORMAT_A_8;
break;
default:
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 23a4722..b7210e1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -53,7 +53,7 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<!-- Physical hardware -->
<uses-permission android:name="android.permission.MANAGE_USB" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_zen_none.xml b/packages/SystemUI/res/drawable/stat_sys_zen_none.xml
new file mode 100644
index 0000000..101e3c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_zen_none.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="19dp"
+ android:height="19dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0C44.0,13.0 35.0,4.0 24.0,4.0zM24.0,40.0c-8.8,0.0 -16.0,-7.2 -16.0,-16.0c0.0,-3.7 1.3,-7.1 3.4,-9.8l22.4,22.4C31.1,38.7 27.7,40.0 24.0,40.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/data_usage.xml b/packages/SystemUI/res/layout/data_usage.xml
new file mode 100644
index 0000000..63d22b2
--- /dev/null
+++ b/packages/SystemUI/res/layout/data_usage.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+
+ <TextView
+ android:id="@+id/usage_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage.Usage" />
+
+ <com.android.systemui.qs.DataUsageGraph
+ android:id="@+id/usage_graph"
+ android:layout_width="match_parent"
+ android:layout_height="8dp"
+ android:layout_marginBottom="@dimen/qs_panel_padding"
+ android:layout_marginTop="8dp" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/usage_carrier_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+
+ <TextView
+ android:id="@+id/usage_info_top_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/usage_period_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary" />
+
+ <TextView
+ android:id="@+id/usage_info_bottom_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 69a22fc..65a6004 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,12 +36,14 @@
<color name="qs_batterymeter_frame_color">#FF404040</color>
<color name="system_primary_color">#ff263238</color>
<color name="system_secondary_color">#ff384248</color>
- <color name="system_accent_color">#ff7fcac3</color>
+ <color name="system_accent_color">#ff80CBC4</color><!-- deep teal 200 -->
<color name="system_error_color">#fff0592b</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
<color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
- <color name="status_bar_clock_color">#FFFFFFFF</color>
+ <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
+ <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
+ <color name="status_bar_clock_color">#33FFFFFF</color>
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e0d48bd..a6e5fea 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -546,6 +546,10 @@
<string name="quick_settings_notifications_label">Notifications</string>
<!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
<string name="quick_settings_flashlight_label">Flashlight</string>
+ <!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_cellular_detail_title">Cellular data</string>
+ <!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_cellular_detail_data_usage">Data usage</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">No recent apps</string>
@@ -584,7 +588,7 @@
<string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
<!-- Zen mode: Alarm warning. [CHAR LIMIT=40] -->
- <string name="zen_alarm_warning">You won\'t hear your alarms</string>
+ <string name="zen_alarm_warning">You won\'t hear alarms or timers</string>
<!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
<string name="zen_no_interruptions">No interruptions</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3f68c31..9984bba6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -169,7 +169,7 @@
<style name="TextAppearance.QS.DetailItemSecondary">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">#7fcac3</item>
+ <item name="android:textColor">@color/system_accent_color</item>
</style>
<style name="TextAppearance.QS.DetailButton">
@@ -196,6 +196,19 @@
<item name="android:fontFamily">sans-serif-medium</item>
</style>
+ <style name="TextAppearance.QS.DataUsage">
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="TextAppearance.QS.DataUsage.Usage">
+ <item name="android:textSize">36sp</item>
+ <item name="android:textColor">@color/system_accent_color</item>
+ </style>
+
+ <style name="TextAppearance.QS.DataUsage.Secondary">
+ <item name="android:textColor">@color/data_usage_secondary</item>
+ </style>
+
<style name="BaseBrightnessDialogContainer">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
new file mode 100644
index 0000000..8b1c778
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.R;
+
+public class DataUsageGraph extends View {
+
+ private final int mBackgroundColor;
+ private final int mUsageColor;
+ private final RectF mTmpRect = new RectF();
+ private final Paint mTmpPaint = new Paint();
+
+ private long mMaxLevel = 1;
+ private long mLimitLevel;
+ private long mWarningLevel;
+ private long mUsageLevel;
+
+ public DataUsageGraph(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mBackgroundColor = context.getResources().getColor(R.color.data_usage_graph_track);
+ mUsageColor = context.getResources().getColor(R.color.system_accent_color);
+ }
+
+ public void setLevels(long maxLevel, long limitLevel, long warningLevel, long usageLevel) {
+ mMaxLevel = Math.max(maxLevel, 1);
+ mLimitLevel = limitLevel;
+ mWarningLevel = warningLevel;
+ mUsageLevel = usageLevel;
+ postInvalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ final RectF r = mTmpRect;
+ final Paint p = mTmpPaint;
+ final int w = getWidth();
+ final int h = getHeight();
+
+ // draw background
+ r.set(0, 0, w, h);
+ p.setColor(mBackgroundColor);
+ canvas.drawRect(r, p);
+
+ // draw usage
+ r.set(0, 0, w * mUsageLevel / (float) mMaxLevel, h);
+ p.setColor(mUsageColor);
+ canvas.drawRect(r, p);
+ }
+}
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 6d91d33..9048ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -20,24 +20,34 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.qs.DataUsageGraph;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+import java.text.DecimalFormat;
+
/** Quick settings tile: Cellular **/
public class CellularTile extends QSTile<QSTile.SignalState> {
private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
"com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
private final NetworkController mController;
+ private final CellularDetailAdapter mDetailAdapter;
public CellularTile(Host host) {
super(host);
mController = host.getNetworkController();
+ mDetailAdapter = new CellularDetailAdapter();
}
@Override
@@ -46,6 +56,11 @@
}
@Override
+ public DetailAdapter getDetailAdapter() {
+ return mDetailAdapter;
+ }
+
+ @Override
public void setListening(boolean listening) {
if (listening) {
mController.addNetworkSignalChangedCallback(mCallback);
@@ -61,7 +76,11 @@
@Override
protected void handleClick() {
- mHost.startSettingsActivity(CELLULAR_SETTINGS);
+ if (mController.isMobileDataSupported()) {
+ showDetail(true);
+ } else {
+ mHost.startSettingsActivity(CELLULAR_SETTINGS);
+ }
}
@Override
@@ -157,5 +176,81 @@
public void onAirplaneModeChanged(boolean enabled) {
// noop
}
+
+ public void onMobileDataEnabled(boolean enabled) {
+ mDetailAdapter.setMobileDataEnabled(enabled);
+ }
};
+
+ private final class CellularDetailAdapter implements DetailAdapter {
+ private static final double KB = 1024;
+ private static final double MB = 1024 * KB;
+ private static final double GB = 1024 * MB;
+
+ private final DecimalFormat FORMAT = new DecimalFormat("#.##");
+
+ @Override
+ public int getTitle() {
+ return R.string.quick_settings_cellular_detail_title;
+ }
+
+ @Override
+ public Boolean getToggleState() {
+ return mController.isMobileDataSupported() ? mController.isMobileDataEnabled() : null;
+ }
+
+ @Override
+ public Intent getSettingsIntent() {
+ return CELLULAR_SETTINGS;
+ }
+
+ @Override
+ public void setToggleState(boolean state) {
+ mController.setMobileDataEnabled(state);
+ }
+
+ @Override
+ public View createDetailView(Context context, View convertView, ViewGroup parent) {
+ final View v = convertView != null ? convertView : LayoutInflater.from(mContext)
+ .inflate(R.layout.data_usage, parent, false);
+ final DataUsageInfo info = mController.getDataUsageInfo();
+ if (info == null) return v;
+ final TextView title = (TextView) v.findViewById(android.R.id.title);
+ title.setText(R.string.quick_settings_cellular_detail_data_usage);
+ final TextView usage = (TextView) v.findViewById(R.id.usage_text);
+ usage.setText(formatBytes(info.usageLevel));
+ final DataUsageGraph graph = (DataUsageGraph) v.findViewById(R.id.usage_graph);
+ graph.setLevels(info.maxLevel, info.limitLevel, info.warningLevel, info.usageLevel);
+ final TextView carrier = (TextView) v.findViewById(R.id.usage_carrier_text);
+ carrier.setText(info.carrier);
+ final TextView period = (TextView) v.findViewById(R.id.usage_period_text);
+ period.setText(info.period);
+ final TextView infoTop = (TextView) v.findViewById(R.id.usage_info_top_text);
+ // TODO
+ final TextView infoBottom = (TextView) v.findViewById(R.id.usage_info_bottom_text);
+ // TODO
+ return v;
+ }
+
+ public void setMobileDataEnabled(boolean enabled) {
+ fireToggleStateChanged(enabled);
+ }
+
+ private String formatBytes(long bytes) {
+ final long b = Math.abs(bytes);
+ double val;
+ String suffix;
+ if (b > 100 * MB) {
+ val = b / GB;
+ suffix = "GB";
+ } else if (b > 100 * KB) {
+ val = b / MB;
+ suffix = "MB";
+ } else {
+ val = b / KB;
+ suffix = "KB";
+ }
+ return FORMAT.format(val * (bytes < 0 ? -1 : 1)) + " " + suffix;
+ }
+ }
}
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 5d1b16c..9e86a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -192,6 +192,11 @@
public void onAirplaneModeChanged(boolean enabled) {
// noop
}
+
+ @Override
+ public void onMobileDataEnabled(boolean enabled) {
+ // noop
+ }
};
private final class WifiDetailAdapter implements DetailAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 186b618..fa8e1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -39,20 +39,18 @@
*/
public class PhoneStatusBarPolicy {
private static final String TAG = "PhoneStatusBarPolicy";
-
- // message codes for the handler
- private static final int EVENT_BATTERY_CLOSE = 4;
-
- private static final int AM_PM_STYLE_NORMAL = 0;
- private static final int AM_PM_STYLE_SMALL = 1;
- private static final int AM_PM_STYLE_GONE = 2;
-
- private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
-
- private static final int INET_CONDITION_THRESHOLD = 50;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean SHOW_SYNC_ICON = false;
+ private static final String SLOT_SYNC_ACTIVE = "sync_active";
+ private static final String SLOT_BLUETOOTH = "bluetooth";
+ private static final String SLOT_TTY = "tty";
+ private static final String SLOT_ZEN = "zen";
+ private static final String SLOT_VOLUME = "volume";
+ private static final String SLOT_CDMA_ERI = "cdma_eri";
+ private static final String SLOT_ALARM_CLOCK = "alarm_clock";
+
private final Context mContext;
private final StatusBarManager mService;
private final Handler mHandler = new Handler();
@@ -61,24 +59,13 @@
// to get broadcasts that it *is* there.
IccCardConstants.State mSimState = IccCardConstants.State.READY;
- // ringer volume
+ private boolean mZenVisible;
private boolean mVolumeVisible;
- // zen mode
private int mZen;
- // bluetooth device status
private boolean mBluetoothEnabled = false;
- private int mLastWifiSignalLevel = -1;
- private boolean mIsWifiConnected = false;
-
- // state of inet connection - 0 not connected, 100 connected
- private int mInetCondition = 0;
-
- // sync state
- // If sync is active the SyncActive icon is displayed. If sync is not active but
- // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -95,7 +82,7 @@
updateBluetooth(intent);
}
else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- updateVolume();
+ updateVolumeZen();
}
else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
updateSimState(intent);
@@ -122,12 +109,12 @@
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
// TTY status
- mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0, null);
- mService.setIconVisibility("tty", false);
+ mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0, null);
+ mService.setIconVisibility(SLOT_TTY, false);
// Cdma Roaming Indicator, ERI
- mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_0, 0, null);
- mService.setIconVisibility("cdma_eri", false);
+ mService.setIcon(SLOT_CDMA_ERI, R.drawable.stat_sys_roaming_cdma_0, 0, null);
+ mService.setIconVisibility(SLOT_CDMA_ERI, false);
// bluetooth status
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -138,38 +125,42 @@
bluetoothIcon = R.drawable.stat_sys_data_bluetooth_connected;
}
}
- mService.setIcon("bluetooth", bluetoothIcon, 0, null);
- mService.setIconVisibility("bluetooth", mBluetoothEnabled);
+ mService.setIcon(SLOT_BLUETOOTH, bluetoothIcon, 0, null);
+ mService.setIconVisibility(SLOT_BLUETOOTH, mBluetoothEnabled);
// Alarm clock
- mService.setIcon("alarm_clock", R.drawable.stat_sys_alarm, 0, null);
- mService.setIconVisibility("alarm_clock", false);
+ mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
+ mService.setIconVisibility(SLOT_ALARM_CLOCK, false);
// Sync state
- mService.setIcon("sync_active", R.drawable.stat_sys_sync, 0, null);
- mService.setIconVisibility("sync_active", false);
+ mService.setIcon(SLOT_SYNC_ACTIVE, R.drawable.stat_sys_sync, 0, null);
+ mService.setIconVisibility(SLOT_SYNC_ACTIVE, false);
// "sync_failing" is obsolete: b/1297963
+ // zen
+ mService.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, 0, null);
+ mService.setIconVisibility(SLOT_ZEN, false);
+
// volume
- mService.setIcon("volume", R.drawable.stat_sys_ringer_silent, 0, null);
- mService.setIconVisibility("volume", false);
- updateVolume();
+ mService.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_silent, 0, null);
+ mService.setIconVisibility(SLOT_VOLUME, false);
+ updateVolumeZen();
}
public void setZenMode(int zen) {
mZen = zen;
- updateVolume();
+ updateVolumeZen();
}
private final void updateAlarm(Intent intent) {
boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
- mService.setIconVisibility("alarm_clock", alarmSet);
+ mService.setIconVisibility(SLOT_ALARM_CLOCK, alarmSet);
}
private final void updateSyncState(Intent intent) {
if (!SHOW_SYNC_ICON) return;
boolean isActive = intent.getBooleanExtra("active", false);
- mService.setIconVisibility("sync_active", isActive);
+ mService.setIconVisibility(SLOT_SYNC_ACTIVE, isActive);
}
private final void updateSimState(Intent intent) {
@@ -200,32 +191,48 @@
}
}
- private final void updateVolume() {
+ private final void updateVolumeZen() {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- final int ringerMode = audioManager.getRingerMode();
- int iconId = 0;
- String contentDescription = null;
- boolean visible = false;
- if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- visible = true;
- iconId = R.drawable.stat_sys_ringer_silent;
- contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
+
+ boolean zenVisible = false;
+ int zenIconId = 0;
+ String zenDescription = null;
+
+ boolean volumeVisible = false;
+ int volumeIconId = 0;
+ String volumeDescription = null;
+
+ if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ zenVisible = true;
+ zenIconId = R.drawable.stat_sys_zen_none;
+ zenDescription = mContext.getString(R.string.zen_no_interruptions);
} else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
- visible = true;
- iconId = R.drawable.stat_sys_zen_important;
- contentDescription = mContext.getString(R.string.zen_important_interruptions);
- } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- visible = true;
- iconId = R.drawable.stat_sys_ringer_vibrate;
- contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
+ zenVisible = true;
+ zenIconId = R.drawable.stat_sys_zen_important;
+ zenDescription = mContext.getString(R.string.zen_important_interruptions);
}
- if (visible) {
- mService.setIcon("volume", iconId, 0, contentDescription);
+ if (mZen != Global.ZEN_MODE_NO_INTERRUPTIONS &&
+ audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
+ volumeVisible = true;
+ volumeIconId = R.drawable.stat_sys_ringer_vibrate;
+ volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
}
- if (visible != mVolumeVisible) {
- mService.setIconVisibility("volume", visible);
- mVolumeVisible = visible;
+
+ if (zenVisible) {
+ mService.setIcon(SLOT_ZEN, zenIconId, 0, zenDescription);
+ }
+ if (zenVisible != mZenVisible) {
+ mService.setIconVisibility(SLOT_ZEN, zenVisible);
+ mZenVisible = zenVisible;
+ }
+
+ if (volumeVisible) {
+ mService.setIcon(SLOT_VOLUME, volumeIconId, 0, volumeDescription);
+ }
+ if (volumeVisible != mVolumeVisible) {
+ mService.setIconVisibility(SLOT_VOLUME, volumeVisible);
+ mVolumeVisible = volumeVisible;
}
}
@@ -250,26 +257,25 @@
return;
}
- mService.setIcon("bluetooth", iconId, 0, contentDescription);
- mService.setIconVisibility("bluetooth", mBluetoothEnabled);
+ mService.setIcon(SLOT_BLUETOOTH, iconId, 0, contentDescription);
+ mService.setIconVisibility(SLOT_BLUETOOTH, mBluetoothEnabled);
}
private final void updateTTY(Intent intent) {
- final String action = intent.getAction();
final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
- if (false) Log.v(TAG, "updateTTY: enabled: " + enabled);
+ if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled);
if (enabled) {
// TTY is on
- if (false) Log.v(TAG, "updateTTY: set TTY on");
- mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0,
+ if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
+ mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0,
mContext.getString(R.string.accessibility_tty_enabled));
- mService.setIconVisibility("tty", true);
+ mService.setIconVisibility(SLOT_TTY, true);
} else {
// TTY is off
- if (false) Log.v(TAG, "updateTTY: set TTY off");
- mService.setIconVisibility("tty", false);
+ if (DEBUG) Log.v(TAG, "updateTTY: set TTY off");
+ mService.setIconVisibility(SLOT_TTY, false);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
new file mode 100644
index 0000000..40549e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class MobileDataController {
+ private static final String TAG = "MobileDataController";
+ private static final boolean DEBUG = true;
+
+ private static final SimpleDateFormat MMM_D = new SimpleDateFormat("MMM d");
+ private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
+
+ private final Context mContext;
+ private final TelephonyManager mTelephonyManager;
+ private final ConnectivityManager mConnectivityManager;
+ private final INetworkStatsService mStatsService;
+
+ private INetworkStatsSession mSession;
+ private Callback mCallback;
+
+ public MobileDataController(Context context) {
+ mContext = context;
+ mTelephonyManager = TelephonyManager.from(context);
+ mConnectivityManager = ConnectivityManager.from(context);
+ mStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ try {
+ mSession = mStatsService.openSession();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to open stats session");
+ mSession = null;
+ }
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ private DataUsageInfo warn(String msg) {
+ Log.w(TAG, "Failed to get data usage, " + msg);
+ return null;
+ }
+
+ public DataUsageInfo getDataUsageInfo() {
+ final String subscriberId = getActiveSubscriberId(mContext);
+ if (subscriberId == null) {
+ return warn("no subscriber id");
+ }
+ if (mSession == null) {
+ return warn("no stats session");
+ }
+ final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ try {
+ final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELDS);
+ final long now = System.currentTimeMillis();
+ // period = last 4 wks for now
+ final long start = now - DateUtils.WEEK_IN_MILLIS * 4;
+ final long end = now;
+ final long callStart = System.currentTimeMillis();
+ final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
+ final long callEnd = System.currentTimeMillis();
+ if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s",
+ new Date(start), new Date(end), new Date(now), callEnd - callStart,
+ historyEntryToString(entry)));
+ if (entry == null) {
+ return warn("no entry data");
+ }
+ final long totalBytes = entry.rxBytes + entry.txBytes;
+ final DataUsageInfo usage = new DataUsageInfo();
+ usage.maxLevel = (long) (totalBytes / .4);
+ usage.usageLevel = totalBytes;
+ usage.period = MMM_D.format(new Date(start)) + " - " + MMM_D.format(new Date(end));
+ return usage;
+ } catch (RemoteException e) {
+ return warn("remote call failed");
+ }
+ }
+
+ private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
+ return entry == null ? null : new StringBuilder("Entry[")
+ .append("bucketDuration=").append(entry.bucketDuration)
+ .append(",bucketStart=").append(entry.bucketStart)
+ .append(",activeTime=").append(entry.activeTime)
+ .append(",rxBytes=").append(entry.rxBytes)
+ .append(",rxPackets=").append(entry.rxPackets)
+ .append(",txBytes=").append(entry.txBytes)
+ .append(",txPackets=").append(entry.txPackets)
+ .append(",operations=").append(entry.operations)
+ .append(']').toString();
+ }
+
+ public void setMobileDataEnabled(boolean enabled) {
+ mTelephonyManager.setDataEnabled(enabled);
+ if (mCallback != null) {
+ mCallback.onMobileDataEnabled(enabled);
+ }
+ }
+
+ public boolean isMobileDataSupported() {
+ // require both supported network and ready SIM
+ return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
+ && mTelephonyManager.getSimState() == SIM_STATE_READY;
+ }
+
+ public boolean isMobileDataEnabled() {
+ return mTelephonyManager.getDataEnabled();
+ }
+
+ private static String getActiveSubscriberId(Context context) {
+ final TelephonyManager tele = TelephonyManager.from(context);
+ final String actualSubscriberId = tele.getSubscriberId();
+ return actualSubscriberId;
+ }
+
+ public interface Callback {
+ void onMobileDataEnabled(boolean enabled);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 7e11369..d058bd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -32,12 +32,17 @@
boolean activityIn, boolean activityOut,
String dataTypeContentDescriptionId, String description, boolean noSim);
void onAirplaneModeChanged(boolean enabled);
+ void onMobileDataEnabled(boolean enabled);
}
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
void connect(AccessPoint ap);
+ boolean isMobileDataSupported();
+ boolean isMobileDataEnabled();
+ void setMobileDataEnabled(boolean enabled);
+ DataUsageInfo getDataUsageInfo();
public interface AccessPointCallback {
void onAccessPointsChanged(AccessPoint[] accessPoints);
@@ -52,4 +57,13 @@
public boolean isConnected;
public int level; // 0 - 5
}
+
+ public static class DataUsageInfo {
+ public String carrier;
+ public String period;
+ public long maxLevel;
+ public long limitLevel;
+ public long warningLevel;
+ public long usageLevel;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 4b94ebd..799b41f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -170,6 +170,7 @@
}
private final WifiAccessPointController mAccessPoints;
+ private final MobileDataController mMobileDataController;
/**
* Construct this controller object and register for updates.
@@ -240,6 +241,19 @@
mLastLocale = mContext.getResources().getConfiguration().locale;
mAccessPoints = new WifiAccessPointController(mContext);
+ mMobileDataController = new MobileDataController(mContext);
+ mMobileDataController.setCallback(new MobileDataController.Callback() {
+ @Override
+ public void onMobileDataEnabled(boolean enabled) {
+ notifyMobileDataEnabled(enabled);
+ }
+ });
+ }
+
+ private void notifyMobileDataEnabled(boolean enabled) {
+ for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) {
+ cb.onMobileDataEnabled(enabled);
+ }
}
public boolean hasMobileDataFeature() {
@@ -322,6 +336,28 @@
}.execute();
}
+ @Override
+ public DataUsageInfo getDataUsageInfo() {
+ final DataUsageInfo info = mMobileDataController.getDataUsageInfo();
+ info.carrier = mNetworkName;
+ return info;
+ }
+
+ @Override
+ public boolean isMobileDataSupported() {
+ return mMobileDataController.isMobileDataSupported();
+ }
+
+ @Override
+ public boolean isMobileDataEnabled() {
+ return mMobileDataController.isMobileDataEnabled();
+ }
+
+ @Override
+ public void setMobileDataEnabled(boolean enabled) {
+ mMobileDataController.setMobileDataEnabled(enabled);
+ }
+
public void refreshSignalCluster(SignalCluster cluster) {
if (mDemoMode) return;
cluster.setWifiIndicators(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index afa5bfe..b9d07d5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -279,8 +279,7 @@
mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
- mAlarmWarning.setVisibility(zenNone && mExpanded && mController != null
- && mController.hasNextAlarm() ? VISIBLE : GONE);
+ mAlarmWarning.setVisibility(zenNone && mExpanded ? VISIBLE : GONE);
if (zenNone) {
mZenSubheadExpanded.setText(R.string.zen_no_interruptions);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b31a3d6..5bfde4d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -6537,10 +6537,10 @@
}
// Pull the Package Manager metadata from the restore set first
- PackageInfo omPackage = new PackageInfo();
- omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ mCurrentPackage = new PackageInfo();
+ mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
- initiateOneRestore(omPackage, 0,
+ initiateOneRestore(mCurrentPackage, 0,
IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
// The PM agent called operationComplete() already, because our invocation
// of it is process-local and therefore synchronous. That means that a
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e865e88..13ad5d2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -60,7 +60,6 @@
import android.database.ContentObserver;
import android.net.CaptivePortalTracker;
import android.net.ConnectivityManager;
-import android.net.DummyDataStateTracker;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
@@ -925,8 +924,6 @@
@Override
public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
switch (config.radio) {
- case TYPE_DUMMY:
- return new DummyDataStateTracker(targetNetworkType, config.name);
case TYPE_WIMAX:
return makeWimaxStateTracker(mContext, mTrackerHandler);
case TYPE_PROXY:
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1da8123..8257f29 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5608,7 +5608,22 @@
}
}
}
-
+
+ @Override
+ public final void mediaResourcesReleased(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack != null) {
+ stack.mediaResourcesReleased(token);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
@Override
public String getCallingPackage(IBinder token) {
synchronized (this) {
@@ -7945,7 +7960,7 @@
checkedGrants = true;
}
userId = handleIncomingUser(callingPid, callingUid, userId,
- false, true, "checkContentProviderPermissionLocked " + cpi.authority, null);
+ false, false, "checkContentProviderPermissionLocked " + cpi.authority, null);
}
if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
cpi.applicationInfo.uid, cpi.exported)
@@ -9347,6 +9362,7 @@
}
if (r.changeWindowTranslucency(true)) {
mWindowManager.setAppFullscreen(token, true);
+ r.task.stack.releaseMediaResources();
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
return true;
}
@@ -9383,6 +9399,38 @@
}
@Override
+ public boolean setMediaPlaying(IBinder token, boolean playing) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ return mStackSupervisor.setMediaPlayingLocked(r, playing);
+ }
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean isBackgroundMediaPlaying(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityStack stack = ActivityRecord.getStackLocked(token);
+ final boolean playing = stack == null ? false : stack.isMediaPlaying();
+ if (ActivityStackSupervisor.DEBUG_MEDIA_VISIBILITY) Slog.d(TAG,
+ "isBackgroundMediaPlaying: stack=" + stack + " playing=" + playing);
+ return playing;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public ActivityOptions getActivityOptions(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index fd2a0b1..d70e8b7 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -203,6 +203,14 @@
pw.print(" resultWho="); pw.print(resultWho);
pw.print(" resultCode="); pw.println(requestCode);
}
+ if (taskDescription.getIcon() != null || taskDescription.getLabel() != null ||
+ taskDescription.getPrimaryColor() != 0) {
+ pw.print(prefix); pw.print("taskDescription:");
+ pw.print(" icon="); pw.print(taskDescription.getIcon());
+ pw.print(" label=\""); pw.print(taskDescription.getLabel()); pw.print("\"");
+ pw.print(" color=");
+ pw.println(Integer.toHexString(taskDescription.getPrimaryColor()));
+ }
if (results != null) {
pw.print(prefix); pw.print("results="); pw.println(results);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ca518f3..c6bba22 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -32,7 +32,6 @@
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
@@ -47,6 +46,7 @@
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.am.ActivityStackSupervisor.ActivityDisplay;
import com.android.server.wm.AppTransition;
import com.android.server.wm.TaskGroup;
import com.android.server.wm.WindowManagerService;
@@ -252,14 +252,14 @@
static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
+ static final int STOP_MEDIA_PLAYING_TIMEOUT_MSG =
+ ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
static class ScheduleDestroyArgs {
final ProcessRecord mOwner;
- final boolean mOomAdj;
final String mReason;
- ScheduleDestroyArgs(ProcessRecord owner, boolean oomAdj, String reason) {
+ ScheduleDestroyArgs(ProcessRecord owner, String reason) {
mOwner = owner;
- mOomAdj = oomAdj;
mReason = reason;
}
}
@@ -320,7 +320,7 @@
case DESTROY_ACTIVITIES_MSG: {
ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
synchronized (mService) {
- destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason);
+ destroyActivitiesLocked(args.mOwner, args.mReason);
}
} break;
case TRANSLUCENT_TIMEOUT_MSG: {
@@ -328,6 +328,15 @@
notifyActivityDrawnLocked(null);
}
} break;
+ case STOP_MEDIA_PLAYING_TIMEOUT_MSG: {
+ synchronized (mService) {
+ final ActivityRecord r = getMediaPlayer();
+ Slog.e(TAG, "Timeout waiting for stopMediaPlaying player=" + r);
+ if (r != null) {
+ mService.killAppAtUsersRequest(r.app, null);
+ }
+ }
+ } break;
}
}
}
@@ -930,11 +939,14 @@
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
r.stopped = true;
r.state = ActivityState.STOPPED;
+ if (mActivityContainer.mActivityDisplay.mMediaPlayingActivity == r) {
+ mStackSupervisor.setMediaPlayingLocked(r, false);
+ }
if (r.finishing) {
r.clearOptionsLocked();
} else {
if (r.configDestroy) {
- destroyActivityLocked(r, true, false, "stop-config");
+ destroyActivityLocked(r, true, "stop-config");
mStackSupervisor.resumeTopActivitiesLocked();
} else {
mStackSupervisor.updatePreviousProcessLocked(r);
@@ -966,8 +978,10 @@
// instance right now, we need to first completely stop
// the current instance before starting the new one.
if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true, false, "pause-config");
- } else {
+ destroyActivityLocked(prev, true, "pause-config");
+ } else if (!isMediaPlaying()) {
+ // If we were playing then resumeTopActivities will release resources before
+ // stopping.
mStackSupervisor.mStoppingActivities.add(prev);
if (mStackSupervisor.mStoppingActivities.size() > 3 ||
prev.frontOfTask && mTaskHistory.size() <= 1) {
@@ -1280,10 +1294,14 @@
case PAUSED:
// This case created for transitioning activities from
// translucent to opaque {@link Activity#convertToOpaque}.
- if (!mStackSupervisor.mStoppingActivities.contains(r)) {
- mStackSupervisor.mStoppingActivities.add(r);
+ if (getMediaPlayer() == r) {
+ releaseMediaResources();
+ } else {
+ if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+ mStackSupervisor.mStoppingActivities.add(r);
+ }
+ mStackSupervisor.scheduleIdleLocked();
}
- mStackSupervisor.scheduleIdleLocked();
break;
default:
@@ -1548,8 +1566,6 @@
// very soon and it would be a waste to let it get killed if it
// happens to be sitting towards the end.
if (next.app != null && next.app.thread != null) {
- // No reason to do full oom adj update here; we'll let that
- // happen whenever it needs to later.
mService.updateLruProcessLocked(next.app, true, null);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2433,7 +2449,7 @@
if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r);
r.state = ActivityState.STOPPED;
if (r.configDestroy) {
- destroyActivityLocked(r, true, false, "stop-except");
+ destroyActivityLocked(r, true, "stop-except");
}
}
}
@@ -2693,7 +2709,7 @@
// If this activity is already stopped, we can just finish
// it right now.
r.makeFinishing();
- boolean activityRemoved = destroyActivityLocked(r, true, oomAdj, "finish-imm");
+ boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
if (activityRemoved) {
mStackSupervisor.resumeTopActivitiesLocked();
}
@@ -2856,6 +2872,9 @@
// Get rid of any pending idle timeouts.
removeTimeoutsForActivityLocked(r);
+ if (getMediaPlayer() == r) {
+ mStackSupervisor.setMediaPlayingLocked(r, false);
+ }
}
private void removeTimeoutsForActivityLocked(ActivityRecord r) {
@@ -2914,13 +2933,13 @@
}
}
- final void scheduleDestroyActivities(ProcessRecord owner, boolean oomAdj, String reason) {
+ final void scheduleDestroyActivities(ProcessRecord owner, String reason) {
Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
- msg.obj = new ScheduleDestroyArgs(owner, oomAdj, reason);
+ msg.obj = new ScheduleDestroyArgs(owner, reason);
mHandler.sendMessage(msg);
}
- final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) {
+ final void destroyActivitiesLocked(ProcessRecord owner, String reason) {
boolean lastIsOpaque = false;
boolean activityRemoved = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -2948,7 +2967,7 @@
if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
+ " resumed=" + mResumedActivity
+ " pausing=" + mPausingActivity);
- if (destroyActivityLocked(r, true, oomAdj, reason)) {
+ if (destroyActivityLocked(r, true, reason)) {
activityRemoved = true;
}
}
@@ -2965,8 +2984,7 @@
* a configuration switch where we destroy the current client-side object
* but then create a new client-side object for this same HistoryRecord.
*/
- final boolean destroyActivityLocked(ActivityRecord r,
- boolean removeFromApp, boolean oomAdj, String reason) {
+ final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(
TAG, "Removing activity from " + reason + ": token=" + r
+ ", app=" + (r.app != null ? r.app.processName : "(null)"));
@@ -3077,6 +3095,49 @@
}
}
+ void releaseMediaResources() {
+ if (isMediaPlaying() && !mHandler.hasMessages(STOP_MEDIA_PLAYING_TIMEOUT_MSG)) {
+ final ActivityRecord r = getMediaPlayer();
+ if (DEBUG_STATES) Slog.d(TAG, "releaseMediaResources activtyDisplay=" +
+ mActivityContainer.mActivityDisplay + " mediaPlayer=" + r + " app=" + r.app +
+ " thread=" + r.app.thread);
+ if (r != null && r.app != null && r.app.thread != null) {
+ try {
+ r.app.thread.scheduleStopMediaPlaying(r.appToken);
+ } catch (RemoteException e) {
+ }
+ mHandler.sendEmptyMessageDelayed(STOP_MEDIA_PLAYING_TIMEOUT_MSG, 500);
+ } else {
+ Slog.e(TAG, "releaseMediaResources: activity " + r + " no longer running");
+ mediaResourcesReleased(r.appToken);
+ }
+ }
+ }
+
+ final void mediaResourcesReleased(IBinder token) {
+ mHandler.removeMessages(STOP_MEDIA_PLAYING_TIMEOUT_MSG);
+ final ActivityRecord r = getMediaPlayer();
+ if (r != null) {
+ mStackSupervisor.mStoppingActivities.add(r);
+ setMediaPlayer(null);
+ }
+ mStackSupervisor.resumeTopActivitiesLocked();
+ }
+
+ boolean isMediaPlaying() {
+ return isAttached() && mActivityContainer.mActivityDisplay.isMediaPlaying();
+ }
+
+ void setMediaPlayer(ActivityRecord r) {
+ if (isAttached()) {
+ mActivityContainer.mActivityDisplay.setMediaPlaying(r);
+ }
+ }
+
+ ActivityRecord getMediaPlayer() {
+ return isAttached() ? mActivityContainer.mActivityDisplay.mMediaPlayingActivity : null;
+ }
+
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
ProcessRecord app, String listName) {
int i = list.size();
@@ -3448,7 +3509,7 @@
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Config is destroying non-running " + r);
- destroyActivityLocked(r, true, false, "config");
+ destroyActivityLocked(r, true, "config");
} else if (r.state == ActivityState.PAUSING) {
// A little annoying: we are waiting for this activity to
// finish pausing. Let's not do anything now, but just
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c4423de..65afd71 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -49,7 +49,6 @@
import android.app.IActivityManager.WaitResult;
import android.app.ResultInfo;
import android.app.StatusBarManager;
-import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -116,6 +115,7 @@
static final boolean DEBUG_APP = DEBUG || false;
static final boolean DEBUG_CONTAINERS = DEBUG || false;
static final boolean DEBUG_IDLE = DEBUG || false;
+ static final boolean DEBUG_MEDIA_VISIBILITY = DEBUG || false;
static final boolean DEBUG_SAVED_STATE = DEBUG || false;
static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
static final boolean DEBUG_STATES = DEBUG || false;
@@ -2102,7 +2102,7 @@
// waiting for the next one to start.
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
- activityRemoved |= r.task.stack.destroyActivityLocked(r, true, false, "finish-idle");
+ activityRemoved |= r.task.stack.destroyActivityLocked(r, true, "finish-idle");
}
if (booting) {
@@ -2298,6 +2298,14 @@
}
IBinder getHomeActivityToken() {
+ ActivityRecord homeActivity = getHomeActivity();
+ if (homeActivity != null) {
+ return homeActivity.appToken;
+ }
+ return null;
+ }
+
+ ActivityRecord getHomeActivity() {
final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
@@ -2306,7 +2314,7 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.isHomeActivity()) {
- return r.appToken;
+ return r;
}
}
}
@@ -2594,6 +2602,42 @@
}
}
+ boolean setMediaPlayingLocked(ActivityRecord r, boolean playing) {
+ final ActivityStack stack = r.task.stack;
+ if (stack == null) {
+ if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlaying: r=" + r + " playing=" +
+ playing + " stack is null");
+ return false;
+ }
+ final boolean isPlaying = stack.isMediaPlaying();
+ if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlayer: r=" + r + " playing=" +
+ playing + " isPlaying=" + isPlaying);
+
+ final ActivityRecord top = topRunningActivityLocked();
+ if (top == null || top == r || (playing == isPlaying)) {
+ if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlaying: quick return");
+ stack.setMediaPlayer(playing ? r : null);
+ return true;
+ }
+
+ // A non-top activity is reporting a visibility change.
+ if (top.fullscreen || top.state != ActivityState.RESUMED || top.app == null ||
+ top.app.thread == null) {
+ // Can't carry out this request.
+ if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlaying: returning top.fullscreen=" +
+ top.fullscreen+ " top.state=" + top.state + " top.app=" + top.app +
+ " top.app.thread=" + top.app.thread);
+ return false;
+ }
+
+ stack.setMediaPlayer(playing ? r : null);
+ try {
+ top.app.thread.scheduleBackgroundMediaPlayingChanged(top.appToken, playing);
+ } catch (RemoteException e) {
+ }
+ return true;
+ }
+
void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -2612,7 +2656,7 @@
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- stack.scheduleDestroyActivities(app, false, reason);
+ stack.scheduleDestroyActivities(app, reason);
}
}
}
@@ -2774,7 +2818,8 @@
boolean needSep = false;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
- pw.print("Display #"); pw.println(activityDisplay.mDisplayId);
+ pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+ pw.println(" (activities from bottom to top):");
ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
final int numStacks = stacks.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
@@ -3584,6 +3629,8 @@
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
+ ActivityRecord mMediaPlayingActivity;
+
ActivityDisplay() {
}
@@ -3615,6 +3662,14 @@
bounds.y = mDisplayInfo.appHeight;
}
+ void setMediaPlaying(ActivityRecord r) {
+ mMediaPlayingActivity = r;
+ }
+
+ boolean isMediaPlaying() {
+ return mMediaPlayingActivity != null;
+ }
+
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ad7c0aa..df12995 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -114,8 +114,7 @@
public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
IConnectivityManager connService, int userId) {
- // TODO: create dedicated TYPE_VPN network type
- super(ConnectivityManager.TYPE_DUMMY);
+ super(ConnectivityManager.TYPE_VPN);
mContext = context;
mCallback = callback;
mConnService = connService;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 6b60ea4..0e6265c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.Arrays;
+import java.util.Objects;
/**
* Holds data about notifications that should not be shared with the
@@ -201,6 +202,10 @@
return mIntercept;
}
+ public boolean isCategory(String category) {
+ return Objects.equals(category, getNotification().category);
+ }
+
/**
* Returns the timestamp to use for time-based sorting in the ranker.
*/
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 92bec14..f74e371 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,6 +21,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -34,6 +35,7 @@
import android.os.IBinder;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
+import android.telecomm.TelecommManager;
import android.util.Slog;
import com.android.internal.R;
@@ -72,17 +74,13 @@
private final ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private ComponentName mDefaultPhoneApp;
private int mZenMode;
private ZenModeConfig mConfig;
private AudioManager mAudioManager;
private int mPreviousRingerMode = -1;
// temporary, until we update apps to provide metadata
- private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.dialer",
- "com.android.phone",
- "com.android.example.notificationshowcase"
- ));
private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.talk",
"com.android.mms",
@@ -224,7 +222,7 @@
public boolean allowDisable(int what, IBinder token, String pkg) {
// TODO(cwren): delete this API before the next release. Bug:15344099
- if (CALL_PACKAGES.contains(pkg)) {
+ if (isDefaultPhoneApp(pkg)) {
return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
}
return true;
@@ -236,6 +234,7 @@
pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
+ pw.print(prefix); pw.print("mDefaultPhoneApp="); pw.println(mDefaultPhoneApp);
}
public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -280,7 +279,7 @@
private boolean isSystem(NotificationRecord record) {
return SYSTEM_PACKAGES.contains(record.sbn.getPackageName())
- && Notification.CATEGORY_SYSTEM.equals(record.getNotification().category);
+ && record.isCategory(Notification.CATEGORY_SYSTEM);
}
private boolean isAlarm(NotificationRecord record) {
@@ -288,7 +287,19 @@
}
private boolean isCall(NotificationRecord record) {
- return CALL_PACKAGES.contains(record.sbn.getPackageName());
+ return isDefaultPhoneApp(record.sbn.getPackageName())
+ || record.isCategory(Notification.CATEGORY_CALL);
+ }
+
+ private boolean isDefaultPhoneApp(String pkg) {
+ if (mDefaultPhoneApp == null) {
+ final TelecommManager telecomm =
+ (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
+ mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
+ Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
+ }
+ return pkg != null && mDefaultPhoneApp != null
+ && pkg.equals(mDefaultPhoneApp.getPackageName());
}
private boolean isMessage(NotificationRecord record) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b65cf72..911c034a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13047,7 +13047,7 @@
/** Called by UserManagerService */
void cleanUpUserLILPw(int userHandle) {
mDirtyUsers.remove(userHandle);
- mSettings.removeUserLPr(userHandle);
+ mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
if (mInstaller != null) {
// Technically, we shouldn't be doing this with the package lock
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ff075e3..81ea72c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3112,7 +3112,7 @@
writePackageRestrictionsLPr(userHandle);
}
- void removeUserLPr(int userId) {
+ void removeUserLPw(int userId) {
Set<Entry<String, PackageSetting>> entries = mPackages.entrySet();
for (Entry<String, PackageSetting> entry : entries) {
entry.getValue().removeUser(userId);
@@ -3123,6 +3123,7 @@
file = getUserPackagesStateBackupFile(userId);
file.delete();
removeCrossProfileIntentFiltersToUserLPr(userId);
+ removeCrossProfilePackagesLPw(userId);
}
void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
@@ -3144,6 +3145,27 @@
}
}
+ public void removeCrossProfilePackagesLPw(int userId) {
+ synchronized(mCrossProfilePackageInfo) {
+ // userId is the source user
+ if (mCrossProfilePackageInfo.get(userId) != null) {
+ mCrossProfilePackageInfo.remove(userId);
+ writePackageRestrictionsLPr(userId);
+ }
+ // userId is the target user
+ int count = mCrossProfilePackageInfo.size();
+ for (int i = 0; i < count; i++) {
+ int sourceUserId = mCrossProfilePackageInfo.keyAt(i);
+ SparseArray<ArrayList<String>> sourceForwardingInfo =
+ mCrossProfilePackageInfo.valueAt(i);
+ if (sourceForwardingInfo.get(userId) != null) {
+ sourceForwardingInfo.remove(userId);
+ writePackageRestrictionsLPr(sourceUserId);
+ }
+ }
+ }
+ }
+
// This should be called (at least) whenever an application is removed
private void setFirstAvailableUid(int uid) {
if (uid > mFirstAvailableUid) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 194e85d..64288a5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -394,16 +394,21 @@
@Override
public void setUserIcon(int userId, Bitmap bitmap) {
checkManageUsersPermission("update users");
- synchronized (mPackagesLock) {
- UserInfo info = mUsers.get(userId);
- if (info == null || info.partial) {
- Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
- return;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mPackagesLock) {
+ UserInfo info = mUsers.get(userId);
+ if (info == null || info.partial) {
+ Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
+ return;
+ }
+ writeBitmapLocked(info, bitmap);
+ writeUserLocked(info);
}
- writeBitmapLocked(info, bitmap);
- writeUserLocked(info);
+ sendUserInfoChangedBroadcast(userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- sendUserInfoChangedBroadcast(userId);
}
private void sendUserInfoChangedBroadcast(int userId) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 02dd1bf..bf767e2 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -485,6 +485,27 @@
}
@Override
+ public void onChannelRetuned(Uri channelUri) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+ }
+ if (sessionState.mSession == null || sessionState.mClient == null) {
+ return;
+ }
+ try {
+ // TODO: Consider adding this channel change in the watch log. When we do
+ // that, how we can protect the watch log from malicious tv inputs should
+ // be addressed. e.g. add a field which represents where the channel change
+ // originated from.
+ sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onChannelRetuned");
+ }
+ }
+ }
+
+ @Override
public void onSessionEvent(String eventType, Bundle eventArgs) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 64d418a..a9d5c72 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -68,6 +68,166 @@
////////////////////////////////////////////////////////////////////////////////
+class BufferProducerThread : public Thread {
+public:
+ BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
+
+ virtual status_t readyToRun();
+
+ void setSurface(const sp<Surface>& surface);
+ void onCaptured(uint32_t seq, bool succeeded);
+ void shutdown();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+ sp<Surface> mSurface;
+ tv_input_device_t* mDevice;
+ int mDeviceId;
+ tv_stream_t mStream;
+ sp<ANativeWindowBuffer_t> mBuffer;
+ enum {
+ CAPTURING,
+ CAPTURED,
+ RELEASED,
+ } mBufferState;
+ uint32_t mSeq;
+ bool mShutdown;
+
+ virtual bool threadLoop();
+
+ void setSurfaceLocked(const sp<Surface>& surface);
+};
+
+BufferProducerThread::BufferProducerThread(
+ tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
+ : Thread(false),
+ mDevice(device),
+ mDeviceId(deviceId),
+ mBuffer(NULL),
+ mBufferState(RELEASED),
+ mSeq(0u),
+ mShutdown(false) {
+ memcpy(&mStream, stream, sizeof(mStream));
+}
+
+status_t BufferProducerThread::readyToRun() {
+ sp<ANativeWindow> anw(mSurface);
+ status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = native_window_set_buffers_dimensions(
+ anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ return NO_ERROR;
+}
+
+void BufferProducerThread::setSurface(const sp<Surface>& surface) {
+ Mutex::Autolock autoLock(&mLock);
+ setSurfaceLocked(surface);
+}
+
+void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
+ if (surface == mSurface) {
+ return;
+ }
+
+ if (mBufferState == CAPTURING) {
+ mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
+ }
+ while (mBufferState == CAPTURING) {
+ status_t err = mCondition.waitRelative(mLock, s2ns(1));
+ if (err != NO_ERROR) {
+ ALOGE("error %d while wating for buffer state to change.", err);
+ break;
+ }
+ }
+ mBuffer.clear();
+ mBufferState = RELEASED;
+
+ mSurface = surface;
+ mCondition.broadcast();
+}
+
+void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
+ Mutex::Autolock autoLock(&mLock);
+ if (seq != mSeq) {
+ ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
+ }
+ if (mBufferState != CAPTURING) {
+ ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
+ }
+ if (succeeded) {
+ mBufferState = CAPTURED;
+ } else {
+ mBuffer.clear();
+ mBufferState = RELEASED;
+ }
+ mCondition.broadcast();
+}
+
+void BufferProducerThread::shutdown() {
+ Mutex::Autolock autoLock(&mLock);
+ mShutdown = true;
+ setSurfaceLocked(NULL);
+ requestExitAndWait();
+}
+
+bool BufferProducerThread::threadLoop() {
+ Mutex::Autolock autoLock(&mLock);
+
+ status_t err = NO_ERROR;
+ if (mSurface == NULL) {
+ err = mCondition.waitRelative(mLock, s2ns(1));
+ // It's OK to time out here.
+ if (err != NO_ERROR && err != TIMED_OUT) {
+ ALOGE("error %d while wating for non-null surface to be set", err);
+ return false;
+ }
+ return true;
+ }
+ sp<ANativeWindow> anw(mSurface);
+ while (mBufferState == CAPTURING) {
+ err = mCondition.waitRelative(mLock, s2ns(1));
+ if (err != NO_ERROR) {
+ ALOGE("error %d while wating for buffer state to change.", err);
+ return false;
+ }
+ }
+ if (mBufferState == CAPTURED && anw != NULL) {
+ err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
+ if (err != NO_ERROR) {
+ ALOGE("error %d while queueing buffer to surface", err);
+ return false;
+ }
+ mBuffer.clear();
+ mBufferState = RELEASED;
+ }
+ if (mBuffer == NULL && !mShutdown && anw != NULL) {
+ ANativeWindowBuffer_t* buffer = NULL;
+ err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
+ if (err != NO_ERROR) {
+ ALOGE("error %d while dequeueing buffer to surface", err);
+ return false;
+ }
+ mBuffer = buffer;
+ mBufferState = CAPTURING;
+ mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
+ buffer->handle, ++mSeq);
+ }
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
class JTvInputHal {
public:
~JTvInputHal();
@@ -79,23 +239,31 @@
const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
private:
+ // Connection between a surface and a stream.
class Connection {
public:
Connection() {}
sp<Surface> mSurface;
+ tv_stream_type_t mStreamType;
+
+ // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
sp<NativeHandle> mSourceHandle;
+ // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
+ sp<BufferProducerThread> mThread;
};
JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
static void notify(
- tv_input_device_t* dev,tv_input_event_t* event, void* data);
+ tv_input_device_t* dev, tv_input_event_t* event, void* data);
void onDeviceAvailable(const tv_input_device_info_t& info);
void onDeviceUnavailable(int deviceId);
void onStreamConfigurationsChanged(int deviceId);
+ void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
+ Mutex mLock;
jweak mThiz;
tv_input_device_t* mDevice;
tv_input_callback_ops_t mCallback;
@@ -153,11 +321,16 @@
// Nothing to do
return NO_ERROR;
}
- if (Surface::isValid(connection.mSurface)) {
+ // Clear the surface in the connection.
+ if (connection.mSurface != NULL) {
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ if (Surface::isValid(connection.mSurface)) {
+ connection.mSurface->setSidebandStream(NULL);
+ }
+ }
connection.mSurface.clear();
}
- connection.mSurface = surface;
- if (connection.mSourceHandle == NULL) {
+ if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
// Need to configure stream
int numConfigs = 0;
const tv_stream_config_t* configs = NULL;
@@ -177,22 +350,32 @@
ALOGE("Cannot find a config with given stream ID: %d", streamId);
return BAD_VALUE;
}
- // TODO: handle buffer producer profile.
- if (configs[configIndex].type !=
- TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
- ALOGE("Profiles other than independent video source is not yet "
- "supported : type = %d", configs[configIndex].type);
- return INVALID_OPERATION;
- }
tv_stream_t stream;
stream.stream_id = configs[configIndex].stream_id;
+ if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ stream.buffer_producer.width = configs[configIndex].max_video_width;
+ stream.buffer_producer.height = configs[configIndex].max_video_height;
+ }
if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
ALOGE("Couldn't add stream");
return UNKNOWN_ERROR;
}
- connection.mSourceHandle = NativeHandle::create(
- stream.sideband_stream_source_handle, false);
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ connection.mSourceHandle = NativeHandle::create(
+ stream.sideband_stream_source_handle, false);
+ } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ if (connection.mThread != NULL) {
+ connection.mThread->shutdown();
+ }
+ connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
+ connection.mThread->run();
+ }
+ }
+ connection.mSurface = surface;
+ if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
connection.mSurface->setSidebandStream(connection.mSourceHandle);
+ } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+ connection.mThread->setSurface(surface);
}
return NO_ERROR;
}
@@ -220,6 +403,12 @@
ALOGE("Couldn't remove stream");
return BAD_VALUE;
}
+
+ // Clear everything
+ if (connection.mThread != NULL) {
+ connection.mThread->shutdown();
+ connection.mThread.clear();
+ }
connection.mSourceHandle.clear();
}
return NO_ERROR;
@@ -249,14 +438,29 @@
case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
thiz->onStreamConfigurationsChanged(event->device_info.device_id);
} break;
+ case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
+ thiz->onCaptured(event->capture_result.device_id,
+ event->capture_result.stream_id,
+ event->capture_result.seq,
+ true /* succeeded */);
+ } break;
+ case TV_INPUT_EVENT_CAPTURE_FAILED: {
+ thiz->onCaptured(event->capture_result.device_id,
+ event->capture_result.stream_id,
+ event->capture_result.seq,
+ false /* succeeded */);
+ } break;
default:
ALOGE("Unrecognizable event");
}
}
void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+ {
+ Mutex::Autolock autoLock(&mLock);
+ mConnections.add(info.device_id, KeyedVector<int, Connection>());
+ }
JNIEnv* env = AndroidRuntime::getJNIEnv();
- mConnections.add(info.device_id, KeyedVector<int, Connection>());
jobject builder = env->NewObject(
gTvInputHardwareInfoBuilderClassInfo.clazz,
@@ -290,13 +494,16 @@
}
void JTvInputHal::onDeviceUnavailable(int deviceId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
- for (size_t i = 0; i < connections.size(); ++i) {
- removeStream(deviceId, connections.keyAt(i));
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ for (size_t i = 0; i < connections.size(); ++i) {
+ removeStream(deviceId, connections.keyAt(i));
+ }
+ connections.clear();
+ mConnections.removeItem(deviceId);
}
- connections.clear();
- mConnections.removeItem(deviceId);
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mThiz,
gTvInputHalClassInfo.deviceUnavailable,
@@ -304,18 +511,36 @@
}
void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
- for (size_t i = 0; i < connections.size(); ++i) {
- removeStream(deviceId, connections.keyAt(i));
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ for (size_t i = 0; i < connections.size(); ++i) {
+ removeStream(deviceId, connections.keyAt(i));
+ }
+ connections.clear();
}
- connections.clear();
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
mThiz,
gTvInputHalClassInfo.streamConfigsChanged,
deviceId);
}
+void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
+ sp<BufferProducerThread> thread;
+ {
+ Mutex::Autolock autoLock(&mLock);
+ KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+ Connection& connection = connections.editValueFor(streamId);
+ if (connection.mThread == NULL) {
+ ALOGE("capture thread not existing.");
+ return;
+ }
+ thread = connection.mThread;
+ }
+ thread->onCaptured(seq, succeeded);
+}
+
////////////////////////////////////////////////////////////////////////////////
static jlong nativeOpen(JNIEnv* env, jobject thiz) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
new file mode 100644
index 0000000..a1240f4
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -0,0 +1,181 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+public class DatabaseHelper extends SQLiteOpenHelper {
+ static final String TAG = "SoundModelDBHelper";
+
+ private static final String NAME = "sound_model.db";
+ private static final int VERSION = 1;
+
+ public static interface KeyphraseContract {
+ public static final String TABLE = "keyphrase";
+ public static final String KEY_ID = "_id";
+ public static final String KEY_RECOGNITION_MODES = "modes";
+ public static final String KEY_LOCALE = "locale";
+ public static final String KEY_HINT_TEXT = "hint_text";
+ public static final String KEY_USERS = "users";
+ public static final String KEY_SOUND_MODEL_ID = "sound_model_id";
+ }
+
+ public static interface SoundModelContract {
+ public static final String TABLE = "sound_model";
+ public static final String KEY_ID = "_id";
+ public static final String KEY_TYPE = "type";
+ public static final String KEY_DATA = "data";
+ }
+
+ // Table Create Statements
+ private static final String CREATE_TABLE_KEYPRHASES = "CREATE TABLE "
+ + KeyphraseContract.TABLE + "("
+ + KeyphraseContract.KEY_ID + " INTEGER PRIMARY KEY,"
+ + KeyphraseContract.KEY_RECOGNITION_MODES + " INTEGER,"
+ + KeyphraseContract.KEY_USERS + " INTEGER,"
+ + KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
+ + KeyphraseContract.KEY_LOCALE + " TEXT,"
+ + KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
+
+ private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
+ + SoundModelContract.TABLE + "("
+ + SoundModelContract.KEY_ID + " TEXT PRIMARY KEY,"
+ + SoundModelContract.KEY_TYPE + " INTEGER,"
+ + SoundModelContract.KEY_DATA + " BLOB" + ")";
+
+ public DatabaseHelper(Context context, CursorFactory factory) {
+ super(context, NAME, null, VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // creating required tables
+ db.execSQL(CREATE_TABLE_KEYPRHASES);
+ db.execSQL(CREATE_TABLE_SOUND_MODEL);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // TODO(sansid): For now, drop older tables and recreate new ones.
+ db.execSQL("DROP TABLE IF EXISTS " + KeyphraseContract.TABLE);
+ db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
+ onCreate(db);
+ }
+
+ /**
+ * TODO(sansid): Change to addOrUpdate to handle changes here.
+ */
+ public void addKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ // Generate a random ID for the model.
+ values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
+ values.put(SoundModelContract.KEY_DATA, soundModel.data);
+ values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
+
+ if (db.insert(SoundModelContract.TABLE, null, values) != -1) {
+ for (Keyphrase keyphrase : soundModel.keyphrases) {
+ addKeyphrase(soundModel.uuid, keyphrase);
+ }
+ } else {
+ Slog.w(TAG, "Failed to persist sound model to database");
+ }
+ }
+
+ /**
+ * TODO(sansid): Change to addOrUpdate to handle changes here.
+ */
+ private void addKeyphrase(UUID modelId, SoundTrigger.Keyphrase keyphrase) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(KeyphraseContract.KEY_ID, keyphrase.id);
+ values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
+ values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
+ values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.text);
+ values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
+ if (db.insert(KeyphraseContract.TABLE, null, values) == -1) {
+ Slog.w(TAG, "Failed to persist keyphrase to database");
+ }
+ }
+
+ /**
+ * Lists all the keyphrase sound models currently registered with the system.
+ */
+ public List<KeyphraseSoundModel> getKephraseSoundModels() {
+ List<KeyphraseSoundModel> models = new ArrayList<>();
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+
+ // looping through all rows and adding to list
+ if (c.moveToFirst()) {
+ do {
+ int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+ if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+ // Ignore non-keyphrase sound models.
+ continue;
+ }
+ String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
+ byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+ // Get all the keyphrases for this this sound model.
+ models.add(new KeyphraseSoundModel(
+ UUID.fromString(id), data, getKeyphrasesForSoundModel(id)));
+ } while (c.moveToNext());
+ }
+ return models;
+ }
+
+ private Keyphrase[] getKeyphrasesForSoundModel(String modelId) {
+ List<Keyphrase> keyphrases = new ArrayList<>();
+ String selectQuery = "SELECT * FROM " + KeyphraseContract.TABLE
+ + " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+
+ // looping through all rows and adding to list
+ if (c.moveToFirst()) {
+ do {
+ int id = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_ID));
+ int modes = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_RECOGNITION_MODES));
+ int[] users = {c.getInt(c.getColumnIndex(KeyphraseContract.KEY_USERS))};
+ String locale = c.getString(c.getColumnIndex(KeyphraseContract.KEY_LOCALE));
+ String hintText = c.getString(c.getColumnIndex(KeyphraseContract.KEY_HINT_TEXT));
+
+ keyphrases.add(new Keyphrase(id, modes, locale, hintText, users));
+ } while (c.moveToNext());
+ }
+ Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
+ keyphrases.toArray(keyphraseArr);
+ return keyphraseArr;
+ }
+}
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index ed61622..28fea79 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -34,20 +34,19 @@
private final Uri mHandle;
private final Bundle mExtras;
private final PhoneAccount mAccount;
+ private final int mVideoState;
- public ConnectionRequest(Uri handle, Bundle extras) {
- this(null, handle, extras);
+ public ConnectionRequest(String callId, Uri handle, Bundle extras, int videoState) {
+ this(null, callId, handle, extras, videoState);
}
- public ConnectionRequest(String callId, Uri handle, Bundle extras) {
- this(null, callId, handle, extras);
- }
-
- public ConnectionRequest(PhoneAccount account, String callId, Uri handle, Bundle extras) {
+ public ConnectionRequest(PhoneAccount account, String callId, Uri handle, Bundle extras,
+ int videoState) {
mCallId = callId;
mHandle = handle;
mExtras = extras;
mAccount = account;
+ mVideoState = videoState;
}
/**
@@ -72,6 +71,19 @@
*/
public Bundle getExtras() { return mExtras; }
+ /**
+ * Determines the video state for the connection.
+ * Valid values: {@link VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+ * {@link VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+ * {@link VideoCallProfile#VIDEO_STATE_TX_ENABLED},
+ * {@link VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
+ *
+ * @return The video state for the connection.
+ */
+ public int getVideoState() {
+ return mVideoState;
+ }
+
public String toString() {
return String.format("PhoneConnectionRequest %s %s",
mHandle == null
@@ -87,7 +99,8 @@
String callId = source.readString();
Uri handle = (Uri) source.readParcelable(getClass().getClassLoader());
Bundle extras = (Bundle) source.readParcelable(getClass().getClassLoader());
- return new ConnectionRequest(callId, handle, extras);
+ int videoState = source.readInt();
+ return new ConnectionRequest(callId, handle, extras, videoState);
}
@Override
@@ -109,4 +122,6 @@
destination.writeString(mCallId);
destination.writeParcelable(mHandle, 0);
destination.writeParcelable(mExtras, 0);
- }}
+ destination.writeInt(mVideoState);
+ }
+}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 73e7d77..4f4941d 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -229,7 +229,7 @@
if (mConnectionId == null) {
String id = UUID.randomUUID().toString();
ConnectionRequest newRequest = new ConnectionRequest(request.getAccount(), id,
- request.getHandle(), request.getExtras());
+ request.getHandle(), request.getExtras(), request.getVideoState());
try {
mConnectionService.call(newRequest);
mConnectionId = id;
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 513a5ee..b9fb40c 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -58,11 +58,15 @@
"android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
/**
- * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
- * the call should be started with video, if possible.
+ * Optional extra for {@link Intent#ACTION_CALL} containing an integer that determines the
+ * desired video state for an outgoing call.
+ * Valid options: {@link VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+ * {@link VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+ * {@link VideoCallProfile#VIDEO_STATE_RX_ENABLED},
+ * {@link VideoCallProfile#VIDEO_STATE_TX_ENABLED}.
*/
- public static final String EXTRA_START_CALL_WITH_VIDEO =
- "android.intent.extra.START_CALL_WITH_VIDEO";
+ public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
+ "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
/**
* Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b29cc12..e4885a1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2300,7 +2300,7 @@
* Input parameters equivalent to TS 27.007 AT+CCHO command.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
* @param AID Application id. See ETSI 102.221 and 101.220.
* @return The logical channel id which is negative on error.
@@ -2322,7 +2322,7 @@
* Input parameters equivalent to TS 27.007 AT+CCHC command.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
* @param channel is the channel id to be closed as retruned by a successful
* iccOpenLogicalChannel.
@@ -2345,7 +2345,7 @@
* Input parameters equivalent to TS 27.007 AT+CGLA command.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
* @param channel is the channel id to be closed as returned by a successful
* iccOpenLogicalChannel.
@@ -2376,7 +2376,7 @@
* Send ENVELOPE to the SIM and return the response.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
* @param content String containing SAT/USAT response in hexadecimal
* format starting with command tag. See TS 102 223 for
@@ -2756,6 +2756,25 @@
}
/**
+ * Get the calculated preferred network type.
+ * Used for debugging incorrect network type.
+ *
+ * @return the preferred network type, defined in RILConstants.java or -1 if
+ * none available.
+ * @hide
+ */
+ public int getCalculatedPreferredNetworkType() {
+ try {
+ return getITelephony().getCalculatedPreferredNetworkType();
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCalculatedPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getCalculatedPreferredNetworkType NPE", ex);
+ }
+ return -1;
+ }
+
+ /**
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
*
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5b6db4d..7bf3b3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -584,6 +584,14 @@
boolean nvResetConfig(int resetType);
/*
+ * Get the calculated preferred network type.
+ * Used for device configuration by some CDMA operators.
+ *
+ * @return the calculated preferred network type, defined in RILConstants.java.
+ */
+ int getCalculatedPreferredNetworkType();
+
+ /*
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
*
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a30fb79..ae16102 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1447,6 +1447,14 @@
*/
public static final int INVALID_ARGS = 8;
+ /**
+ * Passed with {@link ActionListener#onFailure}.
+ * Indicates that the operation failed due to user permissions.
+ *
+ * @hide
+ */
+ public static final int NOT_AUTHORIZED = 9;
+
/** Interface for callback invocation on an application action */
public interface ActionListener {
/** The operation succeeded */
diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
index 61c2b8a..50bec33 100644
--- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
+++ b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
@@ -18,6 +18,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.passpoint.WifiPasspointPolicy;
+import android.net.wifi.passpoint.WifiPasspointCredential;
import android.os.Messenger;
/**
@@ -28,7 +29,17 @@
interface IWifiPasspointManager
{
Messenger getMessenger();
+
int getPasspointState();
+
List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested);
+
+ List<WifiPasspointCredential> getCredentials();
+
+ boolean addCredential(in WifiPasspointCredential cred);
+
+ boolean updateCredential(in WifiPasspointCredential cred);
+
+ boolean removeCredential(in WifiPasspointCredential cred);
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 33ccad5..0a7230f 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -333,7 +333,7 @@
* Set the fully qualified domain name (FQDN) of this Passpoint credential.
* @param fqdn FQDN
*/
- public void setFqdn(String fqdn) {
+ public void setHomeFqdn(String fqdn) {
mHomeSpFqdn = fqdn;
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ddca85e..b9b17eb 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -496,7 +496,11 @@
* @return The list of credentials
*/
public List<WifiPasspointCredential> getCredentials() {
- return null;
+ try {
+ return mService.getCredentials();
+ } catch (RemoteException e) {
+ return null;
+ }
}
/**
@@ -506,7 +510,11 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean addCredential(WifiPasspointCredential cred) {
- return true;
+ try {
+ return mService.addCredential(cred);
+ } catch (RemoteException e) {
+ return false;
+ }
}
/**
@@ -517,7 +525,11 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean updateCredential(WifiPasspointCredential cred) {
- return true;
+ try {
+ return mService.updateCredential(cred);
+ } catch (RemoteException e) {
+ return false;
+ }
}
/**
@@ -528,7 +540,11 @@
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean removeCredential(WifiPasspointCredential cred) {
- return true;
+ try {
+ return mService.removeCredential(cred);
+ } catch (RemoteException e) {
+ return false;
+ }
}
public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) {