Merge "audio: fixed channel count determination from channel mask"
diff --git a/api/current.txt b/api/current.txt
index f9ba781..8548e2f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3212,7 +3212,6 @@
ctor public ActionBar.LayoutParams(int);
ctor public ActionBar.LayoutParams(android.app.ActionBar.LayoutParams);
ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams);
- ctor public ActionBar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
field public int gravity;
}
@@ -12567,6 +12566,7 @@
field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
+ field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
field public static final int LENS_FACING_BACK = 1; // 0x1
field public static final int LENS_FACING_FRONT = 0; // 0x0
@@ -13819,6 +13819,7 @@
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public boolean isSpeakerphoneOn();
+ method public boolean isVolumeFixed();
method public deprecated boolean isWiredHeadsetOn();
method public void loadSoundEffects();
method public void playSoundEffect(int);
@@ -21703,6 +21704,7 @@
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+ field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
@@ -23223,6 +23225,7 @@
field public static final android.net.Uri AUTHORITY_URI;
field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
field public static final java.lang.String DEFERRED_SNIPPETING = "deferred_snippeting";
+ field public static final java.lang.String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
field public static final java.lang.String DIRECTORY_PARAM_KEY = "directory";
field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
field public static final java.lang.String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
@@ -23888,11 +23891,10 @@
field public static final android.net.Uri PROFILE_CONTENT_URI;
}
- public static class ContactsContract.SearchSnippetColumns {
- ctor public ContactsContract.SearchSnippetColumns();
+ public static class ContactsContract.SearchSnippets {
+ ctor public ContactsContract.SearchSnippets();
field public static final java.lang.String DEFERRED_SNIPPETING_KEY = "deferred_snippeting";
field public static final java.lang.String SNIPPET = "snippet";
- field public static final java.lang.String SNIPPET_ARGS_PARAM_KEY = "snippet_args";
}
public static final class ContactsContract.Settings implements android.provider.ContactsContract.SettingsColumns {
@@ -26578,13 +26580,15 @@
public final class SynthesisRequest {
ctor public SynthesisRequest(java.lang.String, android.os.Bundle);
+ ctor public SynthesisRequest(java.lang.CharSequence, android.os.Bundle);
method public int getCallerUid();
+ method public java.lang.CharSequence getCharSequenceText();
method public java.lang.String getCountry();
method public java.lang.String getLanguage();
method public android.os.Bundle getParams();
method public int getPitch();
method public int getSpeechRate();
- method public java.lang.String getText();
+ method public deprecated java.lang.String getText();
method public java.lang.String getVariant();
}
@@ -26594,7 +26598,9 @@
method public int addEarcon(java.lang.String, java.lang.String, int);
method public int addEarcon(java.lang.String, java.lang.String);
method public int addSpeech(java.lang.String, java.lang.String, int);
+ method public int addSpeech(java.lang.CharSequence, java.lang.String, int);
method public int addSpeech(java.lang.String, java.lang.String);
+ method public int addSpeech(java.lang.CharSequence, java.lang.String);
method public boolean areDefaultsEnforced();
method public java.lang.String getDefaultEngine();
method public java.util.Locale getDefaultLanguage();
@@ -26604,8 +26610,10 @@
method public static int getMaxSpeechInputLength();
method public int isLanguageAvailable(java.util.Locale);
method public boolean isSpeaking();
- method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
- method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
+ method public int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+ method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
method public deprecated int setEngineByPackageName(java.lang.String);
method public int setLanguage(java.util.Locale);
method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
@@ -26613,9 +26621,11 @@
method public int setPitch(float);
method public int setSpeechRate(float);
method public void shutdown();
- method public int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+ method public int speak(java.lang.CharSequence, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public deprecated int speak(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
method public int stop();
- method public int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public int synthesizeToFile(java.lang.CharSequence, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String, java.lang.String);
+ method public deprecated int synthesizeToFile(java.lang.String, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
field public static final java.lang.String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_INVALID_REQUEST = -8; // 0xfffffff8
@@ -26659,6 +26669,7 @@
field public static final java.lang.String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
field public static final java.lang.String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
field public static final java.lang.String KEY_PARAM_PAN = "pan";
+ field public static final java.lang.String KEY_PARAM_SESSION_ID = "sessionId";
field public static final java.lang.String KEY_PARAM_STREAM = "streamType";
field public static final java.lang.String KEY_PARAM_UTTERANCE_ID = "utteranceId";
field public static final java.lang.String KEY_PARAM_VOLUME = "volume";
@@ -27451,6 +27462,12 @@
field public static final int SWAP_CALLS = 8; // 0x8
}
+ public final class CallFeatures {
+ field public static final int NONE = 0; // 0x0
+ field public static final int VoLTE = 1; // 0x1
+ field public static final int VoWIFI = 2; // 0x2
+ }
+
public final class CallInfo implements android.os.Parcelable {
ctor public CallInfo(java.lang.String, android.telecomm.CallState, android.net.Uri);
method public int describeContents();
@@ -27487,7 +27504,9 @@
method protected void onAdapterAttached(android.telecomm.CallServiceAdapter);
method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onFeaturesChanged(java.lang.String, int);
method public void onPostDialContinue(java.lang.String, boolean);
+ method public void onPostDialWait(android.telecomm.Connection, java.lang.String);
method public abstract void playDtmfTone(java.lang.String, char);
method public abstract void reject(java.lang.String);
method public abstract void setIncomingCallId(java.lang.String, android.os.Bundle);
@@ -27508,6 +27527,7 @@
method public void setCallVideoProvider(java.lang.String, android.telecomm.CallVideoProvider);
method public void setDialing(java.lang.String);
method public void setDisconnected(java.lang.String, int, java.lang.String);
+ method public void setFeatures(java.lang.String, int);
method public void setOnHold(java.lang.String);
method public void setRequestingRingback(java.lang.String, boolean);
method public void setRinging(java.lang.String);
@@ -27557,12 +27577,12 @@
public abstract class CallVideoClient {
ctor protected CallVideoClient();
- method public abstract void onCallSessionEvent(int);
- method public abstract void onCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
+ method public abstract void onHandleCallSessionEvent(int);
+ method public abstract void onHandleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
method public abstract void onReceiveSessionModifyRequest(android.telecomm.VideoCallProfile);
method public abstract void onReceiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile);
method public abstract void onUpdateCallDataUsage(int);
- method public abstract void onUpdatedPeerDimensions(int, int);
+ method public abstract void onUpdatePeerDimensions(int, int);
field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
field public static final int SESSION_EVENT_TX_START = 3; // 0x3
@@ -27574,22 +27594,24 @@
public abstract class CallVideoProvider {
ctor protected CallVideoProvider();
- method public abstract void requestCallDataUsage();
- method public abstract void requestCameraCapabilities();
- method public abstract void sendSessionModifyRequest(android.telecomm.VideoCallProfile);
- method public abstract void sendSessionModifyResponse(android.telecomm.VideoCallProfile);
- method public abstract void setCamera(java.lang.String);
- method public abstract void setDeviceOrientation(int);
- method public abstract void setDisplaySurface(android.view.Surface);
- method public abstract void setPauseImage(java.lang.String);
- method public abstract void setPreviewSurface(android.view.Surface);
- method public abstract void setZoom(float);
+ method public abstract void onRequestCallDataUsage();
+ method public abstract void onRequestCameraCapabilities();
+ method public abstract void onSendSessionModifyRequest(android.telecomm.VideoCallProfile);
+ method public abstract void onSendSessionModifyResponse(android.telecomm.VideoCallProfile);
+ method public abstract void onSetCallVideoClient(android.telecomm.RemoteCallVideoClient);
+ method public abstract void onSetCamera(java.lang.String);
+ method public abstract void onSetDeviceOrientation(int);
+ method public abstract void onSetDisplaySurface(android.view.Surface);
+ method public abstract void onSetPauseImage(java.lang.String);
+ method public abstract void onSetPreviewSurface(android.view.Surface);
+ method public abstract void onSetZoom(float);
}
public abstract class Connection {
ctor protected Connection();
method public final android.telecomm.CallAudioState getCallAudioState();
method public final java.util.List<android.telecomm.Connection> getChildConnections();
+ method public final int getFeatures();
method public final android.net.Uri getHandle();
method public final android.telecomm.Connection getParentConnection();
method public final int getState();
@@ -27615,6 +27637,7 @@
method public final void setDestroyed();
method public final void setDialing();
method public final void setDisconnected(int, java.lang.String);
+ method public final void setFeatures(int);
method public final void setHandle(android.net.Uri);
method public final void setIsConferenceCapable(boolean);
method public final void setOnHold();
@@ -27665,6 +27688,7 @@
method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
method protected void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.Connection>);
method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
+ method public final void onFeaturesChanged(java.lang.String, int);
method public final void playDtmfTone(java.lang.String, char);
method public final void reject(java.lang.String);
method public final void setIncomingCallId(java.lang.String, android.os.Bundle);
@@ -27711,6 +27735,7 @@
method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
method public int getDisconnectCauseCode();
method public java.lang.String getDisconnectCauseMsg();
+ method public int getFeatures();
method public android.telecomm.GatewayInfo getGatewayInfo();
method public android.net.Uri getHandle();
method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor();
@@ -27734,9 +27759,29 @@
method protected abstract void updateCall(android.telecomm.InCallCall);
}
+ public class RemoteCallVideoClient implements android.os.IBinder.DeathRecipient {
+ method public void binderDied();
+ method public void handleCallSessionEvent(int) throws android.os.RemoteException;
+ method public void handleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities) throws android.os.RemoteException;
+ method public void receiveSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+ method public void receiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+ method public void updateCallDataUsage(int) throws android.os.RemoteException;
+ method public void updatePeerDimensions(int, int) throws android.os.RemoteException;
+ }
+
public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient {
method public void binderDied();
+ method public void requestCallDataUsage() throws android.os.RemoteException;
+ method public void requestCameraCapabilities() throws android.os.RemoteException;
+ method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+ method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
+ method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException;
method public void setCamera(java.lang.String) throws android.os.RemoteException;
+ method public void setDeviceOrientation(int) throws android.os.RemoteException;
+ method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException;
+ method public void setPauseImage(java.lang.String) throws android.os.RemoteException;
+ method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException;
+ method public void setZoom(float) throws android.os.RemoteException;
}
public final class RemoteConnection {
@@ -27746,6 +27791,7 @@
method public void disconnect();
method public int getDisconnectCause();
method public java.lang.String getDisconnectMessage();
+ method public int getState();
method public void hold();
method public void playDtmf(char);
method public void postDialContinue(boolean);
@@ -32264,6 +32310,7 @@
method public final float getY();
method public final float getY(int);
method public final float getYPrecision();
+ method public final boolean isButtonPressed(int);
method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -33845,6 +33892,7 @@
field public static final int FEATURE_RIGHT_ICON = 4; // 0x4
field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002
+ field public static final java.lang.String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "android:navigation:background";
field public static final int PROGRESS_END = 10000; // 0x2710
field public static final int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc
field public static final int PROGRESS_INDETERMINATE_ON = -3; // 0xfffffffd
@@ -33853,6 +33901,7 @@
field public static final int PROGRESS_START = 0; // 0x0
field public static final int PROGRESS_VISIBILITY_OFF = -2; // 0xfffffffe
field public static final int PROGRESS_VISIBILITY_ON = -1; // 0xffffffff
+ field public static final java.lang.String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
}
public static abstract interface Window.Callback {
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 628875f..5c98180 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -1334,8 +1334,14 @@
super(source);
}
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
+ /*
+ * Note for framework developers:
+ *
+ * You might notice that ActionBar.LayoutParams is missing a constructor overload
+ * for MarginLayoutParams. While it may seem like a good idea to add one, at this
+ * point it's dangerous for source compatibility. Upon building against a new
+ * version of the SDK an app can end up statically linking to the new MarginLayoutParams
+ * overload, causing a crash when running on older platform versions with no other changes.
+ */
}
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 6b5549d..fafa948 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -124,6 +124,8 @@
public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
/** @hide */
public static final int ANIM_SCENE_TRANSITION = 5;
+ /** @hide */
+ public static final int ANIM_DEFAULT = 6;
private String mPackageName;
private int mAnimationType = ANIM_NONE;
@@ -344,10 +346,9 @@
* enabled on the calling Activity to cause an exit transition. The same must be in
* the called Activity to get an entering transition.</p>
* @param activity The Activity whose window contains the shared elements.
- * @param sharedElement The View to transition to the started Activity. sharedElement must
- * have a non-null sharedElementName.
- * @param sharedElementName The shared element name as used in the target Activity. This may
- * be null if it has the same name as sharedElement.
+ * @param sharedElement The View to transition to the started Activity.
+ * @param sharedElementName The shared element name as used in the target Activity. This
+ * must not be null.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
* @see android.transition.Transition#setEpicenterCallback(
@@ -374,16 +375,16 @@
* a unique shared element name.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
- * Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}.
* @see android.transition.Transition#setEpicenterCallback(
* android.transition.Transition.EpicenterCallback)
*/
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
Pair<View, String>... sharedElements) {
- if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
- return null;
- }
ActivityOptions opts = new ActivityOptions();
+ if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+ opts.mAnimationType = ANIM_DEFAULT;
+ return opts;
+ }
opts.mAnimationType = ANIM_SCENE_TRANSITION;
ArrayList<String> names = new ArrayList<String>();
@@ -577,9 +578,9 @@
mResultData = null;
mResultCode = 0;
mExitCoordinatorIndex = 0;
+ mAnimationType = otherOptions.mAnimationType;
switch (otherOptions.mAnimationType) {
case ANIM_CUSTOM:
- mAnimationType = otherOptions.mAnimationType;
mCustomEnterResId = otherOptions.mCustomEnterResId;
mCustomExitResId = otherOptions.mCustomExitResId;
mThumbnail = null;
@@ -592,7 +593,6 @@
mAnimationStartedListener = otherOptions.mAnimationStartedListener;
break;
case ANIM_SCALE_UP:
- mAnimationType = otherOptions.mAnimationType;
mStartX = otherOptions.mStartX;
mStartY = otherOptions.mStartY;
mStartWidth = otherOptions.mStartWidth;
@@ -607,7 +607,6 @@
break;
case ANIM_THUMBNAIL_SCALE_UP:
case ANIM_THUMBNAIL_SCALE_DOWN:
- mAnimationType = otherOptions.mAnimationType;
mThumbnail = otherOptions.mThumbnail;
mStartX = otherOptions.mStartX;
mStartY = otherOptions.mStartY;
@@ -620,7 +619,6 @@
mAnimationStartedListener = otherOptions.mAnimationStartedListener;
break;
case ANIM_SCENE_TRANSITION:
- mAnimationType = otherOptions.mAnimationType;
mTransitionReceiver = otherOptions.mTransitionReceiver;
mSharedElementNames = otherOptions.mSharedElementNames;
mIsReturning = otherOptions.mIsReturning;
@@ -642,6 +640,9 @@
* methods that take an options Bundle.
*/
public Bundle toBundle() {
+ if (mAnimationType == ANIM_DEFAULT) {
+ return null;
+ }
Bundle b = new Bundle();
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a480219..b7e64a2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -191,7 +191,9 @@
/** @hide */
public static final int OP_MUTE_MICROPHONE = 44;
/** @hide */
- public static final int _NUM_OP = 45;
+ public static final int OP_TOAST_WINDOW = 45;
+ /** @hide */
+ public static final int _NUM_OP = 46;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -259,7 +261,8 @@
OP_COARSE_LOCATION,
OP_COARSE_LOCATION,
OP_GET_USAGE_STATS,
- OP_MUTE_MICROPHONE
+ OP_MUTE_MICROPHONE,
+ OP_TOAST_WINDOW,
};
/**
@@ -312,6 +315,7 @@
OPSTR_MONITOR_HIGH_POWER_LOCATION,
null,
null,
+ null,
};
/**
@@ -364,6 +368,7 @@
"MONITOR_HIGH_POWER_LOCATION",
"GET_USAGE_STATS",
"OP_MUTE_MICROPHONE",
+ "TOAST_WINDOW",
};
/**
@@ -416,6 +421,7 @@
null, // no permission for high power location monitoring
android.Manifest.permission.PACKAGE_USAGE_STATS,
null, // no permission for muting/unmuting microphone
+ null, // no permission for displaying toasts
};
/**
@@ -448,7 +454,7 @@
null, //READ_ICC_SMS
null, //WRITE_ICC_SMS
null, //WRITE_SETTINGS
- null, //SYSTEM_ALERT_WINDOW
+ UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
null, //ACCESS_NOTIFICATIONS
null, //CAMERA
null, //RECORD_AUDIO
@@ -469,6 +475,60 @@
null, //MONITOR_HIGH_POWER_LOCATION
null, //GET_USAGE_STATS
UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
+ UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
+ };
+
+ /**
+ * This specifies whether each option should allow the system
+ * (and system ui) to bypass the user restriction when active.
+ */
+ private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] {
+ false, //COARSE_LOCATION
+ false, //FINE_LOCATION
+ false, //GPS
+ false, //VIBRATE
+ false, //READ_CONTACTS
+ false, //WRITE_CONTACTS
+ false, //READ_CALL_LOG
+ false, //WRITE_CALL_LOG
+ false, //READ_CALENDAR
+ false, //WRITE_CALENDAR
+ false, //WIFI_SCAN
+ false, //POST_NOTIFICATION
+ false, //NEIGHBORING_CELLS
+ false, //CALL_PHONE
+ false, //READ_SMS
+ false, //WRITE_SMS
+ false, //RECEIVE_SMS
+ false, //RECEIVE_EMERGECY_SMS
+ false, //RECEIVE_MMS
+ false, //RECEIVE_WAP_PUSH
+ false, //SEND_SMS
+ false, //READ_ICC_SMS
+ false, //WRITE_ICC_SMS
+ false, //WRITE_SETTINGS
+ true, //SYSTEM_ALERT_WINDOW
+ false, //ACCESS_NOTIFICATIONS
+ false, //CAMERA
+ false, //RECORD_AUDIO
+ false, //PLAY_AUDIO
+ false, //READ_CLIPBOARD
+ false, //WRITE_CLIPBOARD
+ false, //TAKE_MEDIA_BUTTONS
+ false, //TAKE_AUDIO_FOCUS
+ false, //AUDIO_MASTER_VOLUME
+ false, //AUDIO_VOICE_VOLUME
+ false, //AUDIO_RING_VOLUME
+ false, //AUDIO_MEDIA_VOLUME
+ false, //AUDIO_ALARM_VOLUME
+ false, //AUDIO_NOTIFICATION_VOLUME
+ false, //AUDIO_BLUETOOTH_VOLUME
+ false, //WAKE_LOCK
+ false, //MONITOR_LOCATION
+ false, //MONITOR_HIGH_POWER_LOCATION
+ false, //GET_USAGE_STATS
+ false, // MUTE_MICROPHONE
+ true, // TOAST_WINDOW
};
/**
@@ -520,6 +580,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -575,6 +636,7 @@
false,
false,
false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
@@ -608,6 +670,10 @@
throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length
+ " should be " + _NUM_OP);
}
+ if (sOpAllowSystemRestrictionBypass.length != _NUM_OP) {
+ throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length "
+ + sOpRestrictions.length + " should be " + _NUM_OP);
+ }
for (int i=0; i<_NUM_OP; i++) {
if (sOpToString[i] != null) {
sOpStrToOp.put(sOpToString[i], i);
@@ -649,6 +715,15 @@
}
/**
+ * Retrieve whether the op allows the system (and system ui) to
+ * bypass the user restriction.
+ * @hide
+ */
+ public static boolean opAllowSystemBypassRestriction(int op) {
+ return sOpAllowSystemRestrictionBypass[op];
+ }
+
+ /**
* Retrieve the default mode for the operation.
* @hide
*/
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cb48e58..e2f3c60 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -36,6 +36,7 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -49,6 +50,7 @@
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Process;
@@ -69,6 +71,15 @@
private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
+ UserManager mUserManager;
+
+ UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = UserManager.get(mContext);
+ }
+ return mUserManager;
+ }
+
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
@@ -1500,10 +1511,16 @@
/**
* @hide
*/
- @Override
- public Bitmap getUserIcon(int userId) {
- UserManager um = UserManager.get(mContext);
- return um.getUserIcon(userId);
+ public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
+ if (itemInfo.showUserIcon != UserHandle.USER_NULL) {
+ return new BitmapDrawable(getUserManager().getUserIcon(itemInfo.showUserIcon));
+ }
+ Drawable dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
+ if (dr != null) {
+ dr = getUserManager().getBadgedDrawableForUser(dr,
+ new UserHandle(mContext.getUserId()));
+ }
+ return dr;
}
private final ContextImpl mContext;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index d33b14a..5e18d0f 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -45,6 +45,7 @@
private static final String TAG = "EnterTransitionCoordinator";
private static final long MAX_WAIT_MS = 1000;
+ private static final int MIN_ANIMATION_FRAMES = 2;
private boolean mSharedElementTransitionStarted;
private Activity mActivity;
@@ -56,6 +57,7 @@
private boolean mIsReadyForTransition;
private Bundle mSharedElementsBundle;
private boolean mWasOpaque;
+ private boolean mAreViewsReady;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -80,18 +82,11 @@
}
public void viewInstancesReady(ArrayList<String> accepted, ArrayList<View> localViews) {
- if (mIsReadyForTransition) {
- return;
- }
- viewsReady(mapSharedElements(accepted, localViews));
+ triggerViewsReady(mapSharedElements(accepted, localViews));
}
public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
- if (mIsReadyForTransition) {
- return;
- }
-
- viewsReady(mapNamedElements(accepted, localNames));
+ triggerViewsReady(mapNamedElements(accepted, localNames));
}
@Override
@@ -117,6 +112,27 @@
}
}
+ private void triggerViewsReady(final ArrayMap<String, View> sharedElements) {
+ if (mAreViewsReady) {
+ return;
+ }
+ mAreViewsReady = true;
+ // Ensure the views have been laid out before capturing the views -- we need the epicenter.
+ if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) {
+ viewsReady(sharedElements);
+ } else {
+ sharedElements.valueAt(0).getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sharedElements.valueAt(0).getViewTreeObserver().removeOnPreDrawListener(this);
+ viewsReady(sharedElements);
+ return true;
+ }
+ });
+ }
+ }
+
private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted,
ArrayList<String> localNames) {
ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
@@ -280,9 +296,21 @@
setOriginalImageViewState(originalImageViewState);
if (mResultReceiver != null) {
- mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
+ // We can't trust that the view will disappear on the same frame that the shared
+ // element appears here. Assure that we get at least 2 frames for double-buffering.
+ getDecor().postOnAnimation(new Runnable() {
+ int mAnimations;
+ @Override
+ public void run() {
+ if (mAnimations++ < MIN_ANIMATION_FRAMES) {
+ getDecor().postOnAnimation(this);
+ } else {
+ mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
+ mResultReceiver = null; // all done sending messages.
+ }
+ }
+ });
}
- mResultReceiver = null; // all done sending messages.
}
@Override
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 49f2a5d..93cba0d 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -29,6 +29,7 @@
import android.transition.TransitionManager;
import android.util.ArrayMap;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
@@ -65,8 +66,6 @@
private Bundle mExitSharedElementBundle;
- private ArrayList<View> mSharedElementSnapshots;
-
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
super(activity.getWindow(), names, getListener(activity, isReturning),
@@ -124,6 +123,8 @@
startSharedElementExit();
}
});
+ } else {
+ sharedElementTransitionComplete();
}
}
@@ -131,17 +132,14 @@
Transition transition = getSharedElementExitTransition();
final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
mSharedElementNames);
- mSharedElementSnapshots = createSnapshots(mExitSharedElementBundle, mSharedElementNames);
transition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
- setViewVisibility(mSharedElements, View.INVISIBLE);
- ViewGroupOverlay overlay = getDecor().getOverlay();
- if (mSharedElementSnapshots != null) {
- for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
- overlay.add(mSharedElementSnapshots.get(i));
- }
+ int count = mSharedElements.size();
+ for (int i = 0; i < count; i++) {
+ View sharedElement = mSharedElements.get(i);
+ ((ViewGroup)sharedElement.getParent()).suppressLayout(true);
}
}
});
@@ -158,28 +156,10 @@
getDecor().invalidate();
}
- private static ArrayList<View> copySnapshots(ArrayList<View> snapshots) {
- ArrayList<View> copy = new ArrayList<View>(snapshots.size());
- for (int i = 0; i < snapshots.size(); i++) {
- View view = snapshots.get(i);
- View viewCopy = new View(view.getContext());
- viewCopy.setBackground(view.getBackground());
- copy.add(viewCopy);
- }
- return copy;
- }
-
private void hideSharedElements() {
if (!mIsHidden) {
setViewVisibility(mSharedElements, View.INVISIBLE);
}
- if (mSharedElementSnapshots != null) {
- ViewGroupOverlay overlay = getDecor().getOverlay();
- for (int i = 0; i < mSharedElementSnapshots.size(); i++) {
- overlay.remove(mSharedElementSnapshots.get(i));
- }
- mSharedElementSnapshots = null;
- }
finishIfNecessary();
}
@@ -360,7 +340,8 @@
}
private void finishIfNecessary() {
- if (mIsReturning && mExitNotified && mActivity != null && mSharedElementSnapshots == null) {
+ if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() ||
+ mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
finish();
}
if (!mIsReturning && mExitNotified) {
@@ -382,7 +363,6 @@
mBackgroundAnimator = null;
}
mExitSharedElementBundle = null;
- mSharedElementSnapshots = null;
clearState();
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d18647a..df51ff5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2586,6 +2586,8 @@
* @param packages The list of packages allowed to enter lock task mode
*
* @see Activity#startLockTask()
+ * @see DeviceAdminReceiver#onLockTaskModeChanged(Context, Intent, boolean, String)
+ * @see UserManager#DISALLOW_CREATE_WINDOWS
*/
public void setLockTaskPackages(String[] packages) throws SecurityException {
if (mService != null) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 64d80a0..a94bc41 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -306,6 +306,11 @@
public static final String ACTION_UUID =
"android.bluetooth.device.action.UUID";
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_MAS_INSTANCE =
+ "android.bluetooth.device.action.MAS_INSTANCE";
+
/**
* Broadcast Action: Indicates a failure to retrieve the name of a remote
* device.
@@ -522,14 +527,17 @@
* Prefer BR/EDR transport for GATT connections to remote dual-mode devices
* @hide
*/
- public static final int TRANSPORT_BREDR = 1;
+ public static final int TRANSPORT_BREDR = 1;
/**
* Prefer LE transport for GATT connections to remote dual-mode devices
* @hide
*/
- public static final int TRANSPORT_LE = 2;
+ public static final int TRANSPORT_LE = 2;
+ /** @hide */
+ public static final String EXTRA_MAS_INSTANCE =
+ "android.bluetooth.device.extra.MAS_INSTANCE";
/**
* Lazy initialization. Guaranteed final after first object constructed, or
@@ -995,6 +1003,18 @@
return false;
}
+ /** @hide */
+ public boolean fetchMasInstances() {
+ if (sService == null) {
+ Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances");
+ return false;
+ }
+ try {
+ return sService.fetchRemoteMasInstances(this);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
/** @hide */
public int getServiceChannel(ParcelUuid uuid) {
//TODO(BT)
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 2e993c9d..fbbc2f7 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -32,12 +32,12 @@
* Public API for the Bluetooth GATT Profile server role.
*
* <p>This class provides Bluetooth GATT server role functionality,
- * allowing applications to create and advertise Bluetooth Smart services
- * and characteristics.
+ * allowing applications to create Bluetooth Smart services and
+ * characteristics.
*
* <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
- * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothGatt proxy object.
+ * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance
+ * of this class.
*/
public final class BluetoothGattServer implements BluetoothProfile {
private static final String TAG = "BluetoothGattServer";
@@ -545,7 +545,7 @@
/**
* Add a service to the list of services to be hosted.
*
- * <p>Once a service has been addded to the the list, the service and it's
+ * <p>Once a service has been addded to the the list, the service and its
* included characteristics will be provided by the local device.
*
* <p>If the local device has already exposed services when this function
diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java
new file mode 100644
index 0000000..4459e2c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothMasInstance.java
@@ -0,0 +1,103 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class BluetoothMasInstance implements Parcelable {
+ private final int mId;
+ private final String mName;
+ private final int mChannel;
+ private final int mMsgTypes;
+
+ public BluetoothMasInstance(int id, String name, int channel, int msgTypes) {
+ mId = id;
+ mName = name;
+ mChannel = channel;
+ mMsgTypes = msgTypes;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothMasInstance) {
+ return mId == ((BluetoothMasInstance)o).mId;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mId + (mChannel << 8) + (mMsgTypes << 16);
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" +
+ Integer.toHexString(mMsgTypes);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<BluetoothMasInstance> CREATOR =
+ new Parcelable.Creator<BluetoothMasInstance>() {
+ public BluetoothMasInstance createFromParcel(Parcel in) {
+ return new BluetoothMasInstance(in.readInt(), in.readString(),
+ in.readInt(), in.readInt());
+ }
+ public BluetoothMasInstance[] newArray(int size) {
+ return new BluetoothMasInstance[size];
+ }
+ };
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mId);
+ out.writeString(mName);
+ out.writeInt(mChannel);
+ out.writeInt(mMsgTypes);
+ }
+
+ public static final class MessageType {
+ public static final int EMAIL = 0x01;
+ public static final int SMS_GSM = 0x02;
+ public static final int SMS_CDMA = 0x04;
+ public static final int MMS = 0x08;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getChannel() {
+ return mChannel;
+ }
+
+ public int getMsgTypes() {
+ return mMsgTypes;
+ }
+
+ public boolean msgSupported(int msg) {
+ return (mMsgTypes & msg) != 0;
+ }
+}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index a45c6b8..df6037e 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -67,6 +67,7 @@
int getRemoteClass(in BluetoothDevice device);
ParcelUuid[] getRemoteUuids(in BluetoothDevice device);
boolean fetchRemoteUuids(in BluetoothDevice device);
+ boolean fetchRemoteMasInstances(in BluetoothDevice device);
boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode);
boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[]
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 58f1c84..9f79a89 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -21,6 +21,7 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Printer;
@@ -86,8 +87,15 @@
* {@link PackageManager#GET_META_DATA} flag when requesting the info.
*/
public Bundle metaData;
-
+
+ /**
+ * If different of UserHandle.USER_NULL, The icon of this item will be the one of that user.
+ * @hide
+ */
+ public int showUserIcon;
+
public PackageItemInfo() {
+ showUserIcon = UserHandle.USER_NULL;
}
public PackageItemInfo(PackageItemInfo orig) {
@@ -101,6 +109,7 @@
banner = orig.banner;
logo = orig.logo;
metaData = orig.metaData;
+ showUserIcon = orig.showUserIcon;
}
/**
@@ -143,8 +152,8 @@
* such as the default activity icon.
*/
public Drawable loadIcon(PackageManager pm) {
- if (icon != 0) {
- Drawable dr = pm.getDrawable(packageName, icon, getApplicationInfo());
+ if (icon != 0 || showUserIcon != UserHandle.USER_NULL) {
+ Drawable dr = pm.loadItemIcon(this, getApplicationInfo());
if (dr != null) {
return dr;
}
@@ -288,6 +297,7 @@
dest.writeInt(logo);
dest.writeBundle(metaData);
dest.writeInt(banner);
+ dest.writeInt(showUserIcon);
}
protected PackageItemInfo(Parcel source) {
@@ -300,6 +310,7 @@
logo = source.readInt();
metaData = source.readBundle();
banner = source.readInt();
+ showUserIcon = source.readInt();
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9ac433f..d0b802c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3642,5 +3642,5 @@
/**
* @hide
*/
- public abstract Bitmap getUserIcon(int userId);
+ public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo);
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 1f9d60c..fe3aec9 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,8 +19,6 @@
import android.content.ComponentName;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -136,10 +134,9 @@
public int targetUserId;
/**
- * If true, then loadIcon will return the icon of the target user.
* @hide
*/
- public boolean showTargetUserIcon;
+ public boolean noResourceId;
/**
* @hide Target comes from system process?
@@ -217,10 +214,6 @@
return dr;
}
}
- if (showTargetUserIcon) {
- Bitmap bm = pm.getUserIcon(targetUserId);
- return new BitmapDrawable(bm);
- }
return ci.loadIcon(pm);
}
@@ -232,9 +225,10 @@
* @return The icon associated with this match.
*/
public final int getIconResource() {
+ if (noResourceId) return 0;
if (icon != 0) return icon;
final ComponentInfo ci = getComponentInfo();
- if (ci != null && !showTargetUserIcon) {
+ if (ci != null) {
return ci.getIconResource();
}
return 0;
@@ -312,9 +306,6 @@
sb.append(" targetUserId=");
sb.append(targetUserId);
}
- if (showTargetUserIcon) {
- sb.append(" [showTargetUserIcon]");
- }
sb.append('}');
return sb.toString();
}
@@ -351,8 +342,8 @@
dest.writeInt(icon);
dest.writeString(resolvePackageName);
dest.writeInt(targetUserId);
- dest.writeInt(showTargetUserIcon ? 1 : 0);
dest.writeInt(system ? 1 : 0);
+ dest.writeInt(noResourceId ? 1 : 0);
}
public static final Creator<ResolveInfo> CREATOR
@@ -396,8 +387,8 @@
icon = source.readInt();
resolvePackageName = source.readString();
targetUserId = source.readInt();
- showTargetUserIcon = source.readInt() != 0;
system = source.readInt() != 0;
+ noResourceId = source.readInt() != 0;
}
public static class DisplayNameComparator
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 14e6f92..db08d6c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1797,6 +1797,8 @@
* </ul>
* <p>A LIMITED device may have some or none of the above characteristics.
* To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+ * <p>A LEGACY device does not support per-frame control, manual sensor control, manual
+ * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p>
*
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC
@@ -1806,6 +1808,7 @@
* @see CameraCharacteristics#SYNC_MAX_LATENCY
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
* @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
*/
public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
new Key<Integer>("android.info.supportedHardwareLevel", int.class);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e464f2a..53c8a52 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -577,12 +577,17 @@
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0;
/**
- * <p>This camera device is capable of supporting advanced imaging
- * applications.</p>
+ * <p>This camera device is capable of supporting advanced imaging applications.</p>
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1;
+ /**
+ * <p>This camera device is running in backward compatibility mode.</p>
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ */
+ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2;
+
//
// Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
//
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 4b39092..79f4403 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -33,6 +33,7 @@
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import static android.hardware.camera2.legacy.LegacyExceptionUtils.*;
@@ -271,6 +272,9 @@
return BAD_VALUE;
}
+ List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
+ getSurfaceIds(mConfiguredSurfaces);
+
// Make sure that there all requests have at least 1 surface; all surfaces are non-null
for (CaptureRequest request : requestList) {
if (request.getTargets().isEmpty()) {
@@ -287,7 +291,7 @@
Log.e(TAG, "submitRequestList - must configure " +
" device with valid surfaces before submitting requests");
return INVALID_OPERATION;
- } else if (!mConfiguredSurfaces.contains(surface)) {
+ } else if (!containsSurfaceId(surface, surfaceIds)) {
Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
return BAD_VALUE;
}
@@ -430,6 +434,32 @@
LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
}
+ static long getSurfaceId(Surface surface) {
+ checkNotNull(surface);
+ return nativeGetSurfaceId(surface);
+ }
+
+ static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
+ if (surfaces == null) {
+ throw new NullPointerException("Null argument surfaces");
+ }
+ List<Long> surfaceIds = new ArrayList<>();
+ for (Surface s : surfaces) {
+ long id = getSurfaceId(s);
+ if (id == 0) {
+ throw new IllegalStateException(
+ "Configured surface had null native GraphicBufferProducer pointer!");
+ }
+ surfaceIds.add(id);
+ }
+ return surfaceIds;
+ }
+
+ static boolean containsSurfaceId(Surface s, List<Long> ids) {
+ long id = getSurfaceId(s);
+ return ids.contains(id);
+ }
+
private static native int nativeDetectSurfaceType(Surface surface);
private static native int nativeDetectSurfaceDimens(Surface surface,
@@ -445,4 +475,5 @@
private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
+ private static native long nativeGetSurfaceId(Surface surface);
}
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index e38624a..9969fd2 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -492,19 +492,20 @@
&& (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
return;
}
+
checkGlError("before updateTexImage");
mSurfaceTexture.updateTexImage();
if (targetSurfaces == null) return;
+ List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
for (EGLSurfaceHolder holder : mSurfaces) {
- if (targetSurfaces.contains(holder.surface)) {
+ if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
drawFrame(mSurfaceTexture);
swapBuffers(holder.eglSurface);
}
-
}
for (EGLSurfaceHolder holder : mConversionSurfaces) {
- if (targetSurfaces.contains(holder.surface)) {
+ if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
drawFrame(mSurfaceTexture);
mPBufferPixels.clear();
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index c87b674..d71a411 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -104,7 +104,6 @@
/** Logical address used to indicate the source comes from internal device. */
public static final int ADDR_INTERNAL = 0xFFFF;
- // TODO: Complete the list of CEC messages definition.
public static final int MESSAGE_FEATURE_ABORT = 0x00;
public static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
public static final int MESSAGE_TUNER_STEP_INCREMENT = 0x05;
@@ -190,6 +189,55 @@
public static final int RESULT_EXCEPTION = 5;
public static final int RESULT_INCORRECT_MODE = 6;
+ // Definitions used for setOption(). These should be in sync with the definition
+ // in hardware/libhardware/include/hardware/{hdmi_cec.h,mhl.h}.
+
+ /**
+ * TV gets turned on by incoming <Text/Image View On>. {@code ENABLED} by default.
+ * If set to {@code DISABLED}, TV won't turn on automatically.
+ */
+ public static final int OPTION_CEC_AUTO_WAKEUP = 1;
+
+ /**
+ * If set to {@code DISABLED}, all CEC commands are discarded.
+ *
+ * <p> This option is for internal use only, not supposed to be used by other components.
+ * @hide
+ */
+ public static final int OPTION_CEC_ENABLE = 2;
+
+ /**
+ * If set to {@code DISABLED}, system service yields control of CEC to sub-microcontroller.
+ * If {@code ENABLED}, it take the control back.
+ *
+ * <p> This option is for internal use only, not supposed to be used by other components.
+ * @hide
+ */
+ public static final int OPTION_CEC_SERVICE_CONTROL = 3;
+
+ /**
+ * Put other devices to standby when TV goes to standby. {@code ENABLED} by default.
+ * If set to {@code DISABLED}, TV doesn't send <Standby> to other devices.
+ */
+ public static final int OPTION_CEC_AUTO_DEVICE_OFF = 4;
+
+ /** If set to {@code DISABLED}, TV does not switch ports when mobile device is connected. */
+ public static final int OPTION_MHL_INPUT_SWITCHING = 101;
+
+ /** If set to {@code ENABLED}, TV disables power charging for mobile device. */
+ public static final int OPTION_MHL_POWER_CHARGE = 102;
+
+ /**
+ * If set to {@code DISABLED}, all MHL commands are discarded.
+ *
+ * <p> This option is for internal use only, not supposed to be used by other components.
+ * @hide
+ */
+ public static final int OPTION_MHL_ENABLE = 103;
+
+ public static final int DISABLED = 0;
+ public static final int ENABLED = 1;
+
private static final int[] ADDRESS_TO_TYPE = {
DEVICE_TV, // ADDR_TV
DEVICE_RECORDER, // ADDR_RECORDER_1
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index fbf75d3..f2948e7 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -50,4 +50,5 @@
void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
void setControlEnabled(boolean enabled);
void setArcMode(boolean enabled);
+ void setOption(int option, int value);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a8b324a..05e179d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2208,15 +2208,15 @@
if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
try {
incCallbackHandlerRefCount();
- if (action == LISTEN) {
- networkCallback.networkRequest = mService.listenForNetwork(need,
- new Messenger(sCallbackHandler), new Binder());
- } else {
- networkCallback.networkRequest = mService.requestNetwork(need,
- new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
- }
- if (networkCallback.networkRequest != null) {
- synchronized(sNetworkCallback) {
+ synchronized(sNetworkCallback) {
+ if (action == LISTEN) {
+ networkCallback.networkRequest = mService.listenForNetwork(need,
+ new Messenger(sCallbackHandler), new Binder());
+ } else {
+ networkCallback.networkRequest = mService.requestNetwork(need,
+ new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
+ }
+ if (networkCallback.networkRequest != null) {
sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
}
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index df6057e..1129c9e 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -31,11 +31,15 @@
/** Control UID policies. */
void setUidPolicy(int uid, int policy);
+ void addUidPolicy(int uid, int policy);
+ void removeUidPolicy(int uid, int policy);
int getUidPolicy(int uid);
int[] getUidsWithPolicy(int policy);
boolean isUidForeground(int uid);
+ int[] getPowerSaveAppIdWhitelist();
+
void registerListener(INetworkPolicyListener listener);
void unregisterListener(INetworkPolicyListener listener);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2cd1f9b..a8e7757 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -45,6 +45,8 @@
public static final int POLICY_NONE = 0x0;
/** Reject network usage on metered networks when application in background. */
public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
+ /** Allow network use (metered or not) in the background in battery save mode. */
+ public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
/** All network traffic should be allowed. */
public static final int RULE_ALLOW_ALL = 0x0;
@@ -76,7 +78,7 @@
* Set policy flags for specific UID.
*
* @param policy {@link #POLICY_NONE} or combination of flags like
- * {@link #POLICY_REJECT_METERED_BACKGROUND}.
+ * {@link #POLICY_REJECT_METERED_BACKGROUND}, {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
*/
public void setUidPolicy(int uid, int policy) {
try {
@@ -85,6 +87,30 @@
}
}
+ /**
+ * Add policy flags for specific UID. The given policy bits will be set for
+ * the uid. Policy flags may be either
+ * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+ */
+ public void addUidPolicy(int uid, int policy) {
+ try {
+ mService.addUidPolicy(uid, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Clear/remove policy flags for specific UID. The given policy bits will be set for
+ * the uid. Policy flags may be either
+ * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+ */
+ public void removeUidPolicy(int uid, int policy) {
+ try {
+ mService.removeUidPolicy(uid, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
public int getUidPolicy(int uid) {
try {
return mService.getUidPolicy(uid);
@@ -101,6 +127,14 @@
}
}
+ public int[] getPowerSaveAppIdWhitelist() {
+ try {
+ return mService.getPowerSaveAppIdWhitelist();
+ } catch (RemoteException e) {
+ return new int[0];
+ }
+ }
+
public void registerListener(INetworkPolicyListener listener) {
try {
mService.registerListener(listener);
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index c09492c..c599dfc 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -81,6 +81,13 @@
public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS";
/**
+ * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an
+ * array of {@link NetworkKey}s. Can be obtained with
+ * {@link android.content.Intent#getParcelableArrayExtra(String)}}.
+ */
+ public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
+
+ /**
* Activity action: launch a custom activity for configuring a scorer before enabling it.
* Scorer applications may choose to specify an activity for this action, in which case the
* framework will launch that activity which should return RESULT_OK if scoring was enabled.
@@ -92,11 +99,24 @@
public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
/**
- * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an
- * array of {@link NetworkKey}s. Can be obtained with
- * {@link android.content.Intent#getParcelableArrayExtra(String)}}.
+ * Broadcast action: the active scorer has been changed. Scorer apps may listen to this to
+ * perform initialization once selected as the active scorer, or clean up unneeded resources
+ * if another scorer has been selected. Note that it is unnecessary to clear existing scores as
+ * this is handled by the system.
+ *
+ * <p>The new scorer will be specified in {@link #EXTRA_NEW_SCORER}.
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
*/
- public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SCORER_CHANGED = "android.net.scoring.SCORER_CHANGED";
+
+ /**
+ * Extra used with {@link #ACTION_SCORER_CHANGED} to specify the newly selected scorer's package
+ * name. Will be null if scoring was disabled. Can be obtained with
+ * {@link android.content.Intent#getStringExtra(String)}.
+ */
+ public static final String EXTRA_NEW_SCORER = "newScorer";
private final Context mContext;
private final INetworkScoreService mService;
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 53e87a6..541b700 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.IntentFilter;
+import android.nfc.BeamShareData;
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TechListParcel;
@@ -47,7 +48,8 @@
void setForegroundDispatch(in PendingIntent intent,
in IntentFilter[] filters, in TechListParcel techLists);
void setAppCallback(in IAppCallback callback);
- void invokeBeam();
+ oneway void invokeBeam();
+ oneway void invokeBeamInternal(in BeamShareData shareData);
void dispatch(in Tag tag);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index be098a8..b0397d5 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -29,6 +29,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.nfc.BeamShareData;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
@@ -1274,6 +1275,21 @@
}
/**
+ * @hide
+ */
+ public boolean invokeBeam(BeamShareData shareData) {
+ try {
+ Log.e(TAG, "invokeBeamInternal()");
+ sService.invokeBeamInternal(shareData);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "invokeBeam: NFC process has died.");
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
* Enable NDEF message push over NFC while this Activity is in the foreground.
*
* <p>You must explicitly call this method every time the activity is
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index cabda5d..34568c2 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -16,7 +16,7 @@
* The AidGroup class represents a group of Application Identifiers (AIDs).
*
* <p>An instance of this object can be used with
- * {@link CardEmulation#registerAidGroupForService(android.content.ComponentName, AidGroup)}
+ * {@link CardEmulation#registerAidsForService(android.content.ComponentName, String, java.util.List)}
* to tell the OS which AIDs are handled by your HCE- or SE-based service.
*
* <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
@@ -50,6 +50,11 @@
if (aids.size() > MAX_NUM_AIDS) {
throw new IllegalArgumentException("Too many AIDs in AID group.");
}
+ for (String aid : aids) {
+ if (!ApduServiceInfo.isValidAid(aid)) {
+ throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
+ }
+ }
if (isValidCategory(category)) {
this.category = category;
} else {
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index f379ee8..930cf11 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -351,20 +351,37 @@
}
}
+ /**
+ * A valid AID according to ISO/IEC 7816-4:
+ * <ul>
+ * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
+ * <li>Consist of only hex characters
+ * <li>Additionally, we allow an asterisk at the end, to indicate
+ * a prefix
+ * </ul>
+ */
static boolean isValidAid(String aid) {
if (aid == null)
return false;
- int aidLength = aid.length();
- if (aidLength == 0 || (aidLength % 2) != 0) {
- Log.e(TAG, "AID " + aid + " is not correctly formatted.");
+ // If a prefix AID, the total length must be odd (even # of AID chars + '*')
+ if (aid.endsWith("*") && ((aid.length() % 2) == 0)) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
return false;
}
- // Minimum AID length is 5 bytes, 10 hex chars
- if (aidLength < 10) {
- Log.e(TAG, "AID " + aid + " is shorter than 5 bytes.");
+
+ // If not a prefix AID, the total length must be even (even # of AID chars)
+ if (!aid.endsWith("*") && ((aid.length() % 2) != 0)) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
return false;
}
+
+ // Verify hex characters
+ if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?")) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
return true;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f7d2bfd..21d60c5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -614,6 +614,7 @@
public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29;
public static final int STATE2_WIFI_ON_FLAG = 1<<28;
+ public static final int STATE2_FLASHLIGHT_FLAG = 1<<27;
public static final int MOST_INTERESTING_STATES2 =
STATE2_LOW_POWER_FLAG | STATE2_WIFI_ON_FLAG;
@@ -1235,6 +1236,7 @@
new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"),
+ new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"),
new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss",
new String[] { "0", "1", "2", "3", "4" },
@@ -1370,6 +1372,22 @@
*/
public abstract int getBluetoothStateCount(int bluetoothState, int which);
+ /**
+ * Returns the time in microseconds that the flashlight has been on while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getFlashlightOnTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times that the flashlight has been turned on while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getFlashlightOnCount(int which);
+
public static final int NETWORK_MOBILE_RX_DATA = 0;
public static final int NETWORK_MOBILE_TX_DATA = 1;
public static final int NETWORK_WIFI_RX_DATA = 2;
@@ -1965,6 +1983,9 @@
case SCREEN:
label="scrn";
break;
+ case FLASHLIGHT:
+ label="flashlight";
+ break;
case APP:
uid = bs.uidObj.getUid();
label = "uid";
@@ -2637,6 +2658,10 @@
pw.print(prefix); pw.print(" Screen: "); printmAh(pw, bs.value);
pw.println();
break;
+ case FLASHLIGHT:
+ pw.print(prefix); pw.print(" Flashlight: "); printmAh(pw, bs.value);
+ pw.println();
+ break;
case APP:
pw.print(prefix); pw.print(" Uid ");
UserHandle.formatUid(pw, bs.uidObj.getUid());
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b980e81..a54320f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -27,6 +27,7 @@
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.util.Log;
+import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -278,6 +279,24 @@
*/
public static final String DISALLOW_TELEPHONY = "no_telephony";
+ /**
+ * Key for user restrictions. Specifies that windows besides app windows should not be
+ * created. This will block the creation of the following types of windows.
+ * <li>{@link LayoutParams#TYPE_TOAST}</li>
+ * <li>{@link LayoutParams#TYPE_PHONE}</li>
+ * <li>{@link LayoutParams#TYPE_PRIORITY_PHONE}</li>
+ * <li>{@link LayoutParams#TYPE_SYSTEM_ALERT}</li>
+ * <li>{@link LayoutParams#TYPE_SYSTEM_ERROR}</li>
+ * <li>{@link LayoutParams#TYPE_SYSTEM_OVERLAY}</li>
+ *
+ * <p>The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+
/** @hide */
public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
/** @hide */
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b839613..2f061f1 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -36,6 +36,7 @@
import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
+import android.net.Uri.Builder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.DisplayMetrics;
@@ -171,13 +172,16 @@
* A key to a boolean in the "extras" bundle of the cursor.
* The boolean indicates that the provider did not create a snippet and that the client asking
* for the snippet should do it (true means the snippeting was deferred to the client).
+ *
+ * @see SearchSnippets
*/
public static final String DEFERRED_SNIPPETING = "deferred_snippeting";
/**
- * Key to retrieve the original query on the client side.
+ * Key to retrieve the original deferred snippeting from the cursor on the client side.
*
- * @hide
+ * @see SearchSnippets
+ * @see #DEFERRED_SNIPPETING
*/
public static final String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
@@ -5089,42 +5093,75 @@
/**
* Additional column returned by
* {@link ContactsContract.Contacts#CONTENT_FILTER_URI Contacts.CONTENT_FILTER_URI} explaining
- * why the filter matched the contact. Specifically, it contains the data elements that
- * matched the query. The overall number of words in the snippet can be capped.
+ * why the filter matched the contact. This column will contain extracts from the contact's
+ * constituent {@link Data Data} items, formatted in a way that indicates the section of the
+ * snippet that matched the filter.
+ *
+ * <p>
+ * The following example searches for all contacts that match the query "presi" and requests
+ * the snippet column as well.
+ * <pre>
+ * Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
+ * builder.appendPath("presi");
+ * // Defer snippeting to the client side if possible, for performance reasons.
+ * builder.appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY,"1");
+ *
+ * Cursor cursor = getContentResolver().query(builder.build());
+ *
+ * Bundle extras = cursor.getExtras();
+ * if (extras.getBoolean(ContactsContract.DEFERRED_SNIPPETING)) {
+ * // Do our own snippet formatting.
+ * // For a contact with the email address (president@organization.com), the snippet
+ * // column will contain the string "president@organization.com".
+ * } else {
+ * // The snippet has already been pre-formatted, we can display it as is.
+ * // For a contact with the email address (president@organization.com), the snippet
+ * // column will contain the string "[presi]dent@organization.com".
+ * }
+ * </pre>
+ * </p>
*/
- public static class SearchSnippetColumns {
+ public static class SearchSnippets {
/**
- * The search snippet constructed according to the SQLite rules, see
- * http://www.sqlite.org/fts3.html#snippet
+ * The search snippet constructed by SQLite snippeting functionality.
* <p>
- * The snippet may contain (parts of) several data elements comprising
- * the contact.
+ * The snippet may contain (parts of) several data elements belonging to the contact,
+ * with the matching parts optionally surrounded by special characters that indicate the
+ * start and end of matching text.
+ *
+ * For example, if a contact has an address "123 Main Street", using a filter "mai" would
+ * return the formatted snippet "123 [Mai]n street".
+ *
+ * @see <a href="http://www.sqlite.org/fts3.html#snippet">
+ * http://www.sqlite.org/fts3.html#snippet</a>
*/
public static final String SNIPPET = "snippet";
-
/**
* Comma-separated parameters for the generation of the snippet:
* <ul>
- * <li>The "start match" text. Default is <b></li>
- * <li>The "end match" text. Default is </b></li>
- * <li>The "ellipsis" text. Default is <b>...</b></li>
+ * <li>The "start match" text. Default is '['</li>
+ * <li>The "end match" text. Default is ']'</li>
+ * <li>The "ellipsis" text. Default is "..."</li>
* <li>Maximum number of tokens to include in the snippet. Can be either
* a positive or a negative number: A positive number indicates how many
* tokens can be returned in total. A negative number indicates how many
* tokens can be returned per occurrence of the search terms.</li>
* </ul>
+ *
+ * @hide
*/
public static final String SNIPPET_ARGS_PARAM_KEY = "snippet_args";
/**
- * A key to ask the provider to defer the snippeting to the client if possible.
- * Value of 1 implies true, 0 implies false when 0 is the default.
+ * The key to ask the provider to defer the formatting of the snippet to the client if
+ * possible, for performance reasons.
+ * A value of 1 indicates true, 0 indicates false. False is the default.
* When a cursor is returned to the client, it should check for an extra with the name
* {@link ContactsContract#DEFERRED_SNIPPETING} in the cursor. If it exists, the client
- * should do its own snippeting. If it doesn't exist, the snippet column in the cursor
- * should already contain a snippetized string.
+ * should do its own formatting of the snippet. If it doesn't exist, the snippet column
+ * in the cursor should already contain a formatted snippet.
*/
public static final String DEFERRED_SNIPPETING_KEY = "deferred_snippeting";
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 03ce4e0..f3c26c8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -154,6 +154,7 @@
final Rect mWinFrame = new Rect();
final Rect mOverscanInsets = new Rect();
final Rect mContentInsets = new Rect();
+ final Rect mStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
final WindowManager.LayoutParams mLayout
@@ -253,7 +254,8 @@
final BaseIWindow mWindow = new BaseIWindow() {
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
- Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+ Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+ Configuration newConfig) {
Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0);
mCaller.sendMessage(msg);
@@ -628,7 +630,7 @@
final int relayoutResult = mSession.relayout(
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
- mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
+ mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface);
if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ ", frame=" + mWinFrame);
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
index 03e53ec..c13b47f 100644
--- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
@@ -16,9 +16,12 @@
package android.speech.tts;
import android.content.Context;
+import android.media.AudioSystem;
+import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.ConditionVariable;
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
import android.util.Log;
@@ -27,7 +30,7 @@
private final Context mContext;
private final Uri mUri;
- private final int mStreamType;
+ private final AudioOutputParams mAudioParams;
private final ConditionVariable mDone;
private MediaPlayer mPlayer;
@@ -35,12 +38,12 @@
AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher,
Object callerIdentity,
- Context context, Uri uri, int streamType) {
+ Context context, Uri uri, AudioOutputParams audioParams) {
super(dispatcher, callerIdentity);
mContext = context;
mUri = uri;
- mStreamType = streamType;
+ mAudioParams = audioParams;
mDone = new ConditionVariable();
mPlayer = null;
@@ -73,7 +76,11 @@
mDone.open();
}
});
- mPlayer.setAudioStreamType(mStreamType);
+ mPlayer.setAudioStreamType(mAudioParams.mStreamType);
+ setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
+ if (mAudioParams.mSessionId != AudioSystem.AUDIO_SESSION_ALLOCATE) {
+ mPlayer.setAudioSessionId(mAudioParams.mSessionId);
+ }
mPlayer.start();
mDone.block();
finish();
@@ -89,6 +96,23 @@
}
}
+ private static void setupVolume(MediaPlayer player, float volume, float pan) {
+ final float vol = clip(volume, 0.0f, 1.0f);
+ final float panning = clip(pan, -1.0f, 1.0f);
+
+ float volLeft = vol, volRight = vol;
+ if (panning > 0.0f) {
+ volLeft *= (1.0f - panning);
+ } else if (panning < 0.0f) {
+ volRight *= (1.0f + panning);
+ }
+ player.setVolume(volLeft, volRight);
+ }
+
+ private static final float clip(float value, float min, float max) {
+ return value < min ? min : (value < max ? value : max);
+ }
+
private void finish() {
try {
mPlayer.stop();
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index 92bb0ac..b405de0 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -4,6 +4,7 @@
import android.media.AudioFormat;
import android.media.AudioTrack;
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.util.Log;
/**
@@ -44,12 +45,11 @@
private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
- private final int mStreamType;
+ private final AudioOutputParams mAudioParams;
private final int mSampleRateInHz;
private final int mAudioFormat;
private final int mChannelCount;
- private final float mVolume;
- private final float mPan;
+
private final int mBytesPerFrame;
/**
@@ -73,15 +73,14 @@
private AudioTrack mAudioTrack;
private volatile boolean mStopped;
- BlockingAudioTrack(int streamType, int sampleRate,
- int audioFormat, int channelCount,
- float volume, float pan) {
- mStreamType = streamType;
+ private int mSessionId;
+
+ BlockingAudioTrack(AudioOutputParams audioParams, int sampleRate,
+ int audioFormat, int channelCount) {
+ mAudioParams = audioParams;
mSampleRateInHz = sampleRate;
mAudioFormat = audioFormat;
mChannelCount = channelCount;
- mVolume = volume;
- mPan = pan;
mBytesPerFrame = AudioFormat.getBytesPerSample(mAudioFormat) * mChannelCount;
mIsShortUtterance = false;
@@ -215,8 +214,9 @@
= AudioTrack.getMinBufferSize(mSampleRateInHz, channelConfig, mAudioFormat);
int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
- AudioTrack audioTrack = new AudioTrack(mStreamType, mSampleRateInHz, channelConfig,
- mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
+ AudioTrack audioTrack = new AudioTrack(mAudioParams.mStreamType, mSampleRateInHz,
+ channelConfig, mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
+ mAudioParams.mSessionId);
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.w(TAG, "Unable to create audio track.");
audioTrack.release();
@@ -225,7 +225,7 @@
mAudioBufferSize = bufferSizeInBytes;
- setupVolume(audioTrack, mVolume, mPan);
+ setupVolume(audioTrack, mAudioParams.mVolume, mAudioParams.mPan);
return audioTrack;
}
@@ -328,19 +328,11 @@
}
private static final long clip(long value, long min, long max) {
- if (value < min) {
- return min;
- }
-
- if (value > max) {
- return max;
- }
-
- return value;
+ return value < min ? min : (value < max ? value : max);
}
- private static float clip(float value, float min, float max) {
- return value > max ? max : (value < min ? min : value);
+ private static final float clip(float value, float min, float max) {
+ return value < min ? min : (value < max ? value : max);
}
}
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
index 4d322df..694f25a 100644
--- a/core/java/android/speech/tts/ITextToSpeechService.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -36,8 +36,10 @@
* @param text The text to synthesize.
* @param queueMode Determines what to do to requests already in the queue.
* @param param Request parameters.
+ * @param utteranceId Unique identifier of synthesized utterance.
*/
- int speak(in IBinder callingInstance, in String text, in int queueMode, in Bundle params);
+ int speak(in IBinder callingInstance, in CharSequence text, in int queueMode, in Bundle params,
+ String utteranceId);
/**
* Tells the engine to synthesize some speech and write it to a file.
@@ -47,10 +49,11 @@
* @param text The text to synthesize.
* @param fileDescriptor The file descriptor to write the synthesized audio to. Has to be
writable.
+ * @param utteranceId Unique identifier of synthesized utterance.
* @param param Request parameters.
*/
- int synthesizeToFileDescriptor(in IBinder callingInstance, in String text,
- in ParcelFileDescriptor fileDescriptor, in Bundle params);
+ int synthesizeToFileDescriptor(in IBinder callingInstance, in CharSequence text,
+ in ParcelFileDescriptor fileDescriptor, in Bundle params, String utteranceId);
/**
* Plays an existing audio resource.
@@ -59,9 +62,11 @@
* TextToSpeech object.
* @param audioUri URI for the audio resource (a file or android.resource URI)
* @param queueMode Determines what to do to requests already in the queue.
+ * @param utteranceId Unique identifier of synthesized utterance.
* @param param Request parameters.
*/
- int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params);
+ int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params,
+ String utteranceId);
/**
* Plays silence.
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 7e68d27..f850f10 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts;
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
import android.util.Log;
@@ -28,23 +29,7 @@
private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
- /**
- * Audio stream type. Must be one of the STREAM_ contants defined in
- * {@link android.media.AudioManager}.
- */
- private final int mStreamType;
-
- /**
- * Volume, in the range [0.0f, 1.0f]. The default value is
- * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
- */
- private final float mVolume;
-
- /**
- * Left/right position of the audio, in the range [-1.0f, 1.0f].
- * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
- */
- private final float mPan;
+ private final AudioOutputParams mAudioParams;
/**
* Guards {@link #mAudioTrackHandler}, {@link #mItem} and {@link #mStopped}.
@@ -65,13 +50,11 @@
private final Object mCallerIdentity;
private final AbstractEventLogger mLogger;
- PlaybackSynthesisCallback(int streamType, float volume, float pan,
- AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher,
- Object callerIdentity, AbstractEventLogger logger, boolean clientIsUsingV2) {
+ PlaybackSynthesisCallback(AudioOutputParams audioParams, AudioPlaybackHandler audioTrackHandler,
+ UtteranceProgressDispatcher dispatcher, Object callerIdentity,
+ AbstractEventLogger logger, boolean clientIsUsingV2) {
super(clientIsUsingV2);
- mStreamType = streamType;
- mVolume = volume;
- mPan = pan;
+ mAudioParams = audioParams;
mAudioTrackHandler = audioTrackHandler;
mDispatcher = dispatcher;
mCallerIdentity = callerIdentity;
@@ -161,7 +144,7 @@
return TextToSpeech.ERROR;
}
SynthesisPlaybackQueueItem item = new SynthesisPlaybackQueueItem(
- mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+ mAudioParams, sampleRateInHz, audioFormat, channelCount,
mDispatcher, mCallerIdentity, mLogger);
mAudioTrackHandler.enqueue(item);
mItem = item;
diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
index 104f486..7423933 100644
--- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts;
+import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
import android.util.Log;
@@ -62,9 +63,8 @@
private final BlockingAudioTrack mAudioTrack;
private final AbstractEventLogger mLogger;
- SynthesisPlaybackQueueItem(int streamType, int sampleRate,
- int audioFormat, int channelCount,
- float volume, float pan, UtteranceProgressDispatcher dispatcher,
+ SynthesisPlaybackQueueItem(AudioOutputParams audioParams, int sampleRate,
+ int audioFormat, int channelCount, UtteranceProgressDispatcher dispatcher,
Object callerIdentity, AbstractEventLogger logger) {
super(dispatcher, callerIdentity);
@@ -74,8 +74,7 @@
mDone = false;
mStatusCode = TextToSpeech.SUCCESS;
- mAudioTrack = new BlockingAudioTrack(streamType, sampleRate, audioFormat,
- channelCount, volume, pan);
+ mAudioTrack = new BlockingAudioTrack(audioParams, sampleRate, audioFormat, channelCount);
mLogger = logger;
}
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index 12a026b..eaacc06 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -34,7 +34,7 @@
* and {@link TextToSpeech#synthesizeToFile}.
*/
public final class SynthesisRequest {
- private final String mText;
+ private final CharSequence mText;
private final Bundle mParams;
private String mLanguage;
private String mCountry;
@@ -49,10 +49,25 @@
mParams = new Bundle(params);
}
+ public SynthesisRequest(CharSequence text, Bundle params) {
+ mText = text;
+ // Makes a copy of params.
+ mParams = new Bundle(params);
+ }
+
+ /**
+ * Gets the text which should be synthesized.
+ * @deprecated As of API level 20, replaced by {@link #getCharSequenceText}.
+ */
+ @Deprecated
+ public String getText() {
+ return mText.toString();
+ }
+
/**
* Gets the text which should be synthesized.
*/
- public String getText() {
+ public CharSequence getCharSequenceText() {
return mText;
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index a338c19..e1c1767b 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -564,6 +564,17 @@
* @see TextToSpeech#getFeatures(java.util.Locale)
*/
public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
+
+ /**
+ * Parameter key to specify an audio session identifier (obtained from
+ * {@link AudioManager#allocateAudioSessionId()}) that will be used by the request audio
+ * output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect}
+ * objects with the synthesis (or earcon) output.
+ *
+ * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#playEarcon(String, int, HashMap)
+ */
+ public static final String KEY_PARAM_SESSION_ID = "sessionId";
}
private final Context mContext;
@@ -582,7 +593,7 @@
// too.
private final boolean mUseFallback;
private final Map<String, Uri> mEarcons;
- private final Map<String, Uri> mUtterances;
+ private final Map<CharSequence, Uri> mUtterances;
private final Bundle mParams = new Bundle();
private final TtsEngines mEnginesHelper;
private final String mPackageName;
@@ -633,7 +644,7 @@
mUseFallback = useFallback;
mEarcons = new HashMap<String, Uri>();
- mUtterances = new HashMap<String, Uri>();
+ mUtterances = new HashMap<CharSequence, Uri>();
mUtteranceProgressListener = null;
mEnginesHelper = new TtsEngines(mContext);
@@ -814,6 +825,40 @@
}
/**
+ * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text
+ * and a sound resource in a package. After a call to this method, subsequent calls to
+ * {@link #speak(String, int, HashMap)} will play the specified sound resource
+ * if it is available, or synthesize the text it is missing.
+ *
+ * @param text
+ * The string of text. Example: <code>"south_south_east"</code>
+ *
+ * @param packagename
+ * Pass the packagename of the application that contains the
+ * resource. If the resource is in your own application (this is
+ * the most common case), then put the packagename of your
+ * application here.<br/>
+ * Example: <b>"com.google.marvin.compass"</b><br/>
+ * The packagename can be found in the AndroidManifest.xml of
+ * your application.
+ * <p>
+ * <code><manifest xmlns:android="..."
+ * package="<b>com.google.marvin.compass</b>"></code>
+ * </p>
+ *
+ * @param resourceId
+ * Example: <code>R.raw.south_south_east</code>
+ *
+ * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ */
+ public int addSpeech(CharSequence text, String packagename, int resourceId) {
+ synchronized (mStartLock) {
+ mUtterances.put(text, makeResourceUri(packagename, resourceId));
+ return SUCCESS;
+ }
+ }
+
+ /**
* Adds a mapping between a string of text and a sound file. Using this, it
* is possible to add custom pronounciations for a string of text.
* After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
@@ -835,6 +880,28 @@
}
}
+ /**
+ * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file.
+ * Using this, it is possible to add custom pronounciations for a string of text.
+ * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
+ * will play the specified sound resource if it is available, or synthesize the text it is
+ * missing.
+ *
+ * @param text
+ * The string of text. Example: <code>"south_south_east"</code>
+ * @param filename
+ * The full path to the sound file (for example:
+ * "/sdcard/mysounds/hello.wav")
+ *
+ * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+ */
+ public int addSpeech(CharSequence text, String filename) {
+ synchronized (mStartLock) {
+ mUtterances.put(text, Uri.parse(filename));
+ return SUCCESS;
+ }
+ }
+
/**
* Adds a mapping between a string of text and a sound resource in a
@@ -899,6 +966,51 @@
}
/**
+ * Speaks the text using the specified queuing strategy and speech parameters, the text may
+ * be spanned with TtsSpans.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+ *
+ * @param text The string of text to be spoken. No longer than
+ * {@link #getMaxSpeechInputLength()} characters.
+ * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_STREAM},
+ * {@link Engine#KEY_PARAM_VOLUME},
+ * {@link Engine#KEY_PARAM_PAN}.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
+ * @param utteranceId An unique identifier for this request.
+ *
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
+ */
+ public int speak(final CharSequence text,
+ final int queueMode,
+ final HashMap<String, String> params,
+ final String utteranceId) {
+ return runAction(new Action<Integer>() {
+ @Override
+ public Integer run(ITextToSpeechService service) throws RemoteException {
+ Uri utteranceUri = mUtterances.get(text);
+ if (utteranceUri != null) {
+ return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
+ getParams(params), utteranceId);
+ } else {
+ return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
+ utteranceId);
+ }
+ }
+ }, ERROR, "speak");
+ }
+
+ /**
* Speaks the string using the specified queuing strategy and speech parameters.
* This method is asynchronous, i.e. the method just adds the request to the queue of TTS
* requests and then returns. The synthesis might not have finished (or even started!) at the
@@ -922,20 +1034,51 @@
* engine named "com.svox.pico" if it is being used.
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
+ * @deprecated As of API level 20, replaced by
+ * {@link #speak(CharSequence, int, HashMap, String)}.
*/
+ @Deprecated
public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+ return speak(text, queueMode, params,
+ params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+ }
+
+ /**
+ * Plays the earcon using the specified queueing mode and parameters.
+ * The earcon must already have been added with {@link #addEarcon(String, String)} or
+ * {@link #addEarcon(String, String, int)}.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+ *
+ * @param earcon The earcon that should be played
+ * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_STREAM},
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
+ *
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
+ */
+ public int playEarcon(final String earcon, final int queueMode,
+ final HashMap<String, String> params, final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
- Uri utteranceUri = mUtterances.get(text);
- if (utteranceUri != null) {
- return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
- getParams(params));
- } else {
- return service.speak(getCallerIdentity(), text, queueMode, getParams(params));
+ Uri earconUri = mEarcons.get(earcon);
+ if (earconUri == null) {
+ return ERROR;
}
+ return service.playAudio(getCallerIdentity(), earconUri, queueMode,
+ getParams(params), utteranceId);
}
- }, ERROR, "speak");
+ }, ERROR, "playEarcon");
}
/**
@@ -961,20 +1104,46 @@
* engine named "com.svox.pico" if it is being used.
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
+ * @deprecated As of API level 20, replaced by
+ * {@link #playEarcon(String, int, HashMap, String)}.
*/
+ @Deprecated
public int playEarcon(final String earcon, final int queueMode,
final HashMap<String, String> params) {
+ return playEarcon(earcon, queueMode, params,
+ params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+ }
+
+ /**
+ * Plays silence for the specified amount of time using the specified
+ * queue mode.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+ *
+ * @param durationInMs The duration of the silence.
+ * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+ * @param params Parameters for the request. Can be null.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
+ * @param utteranceId An unique identifier for this request.
+ *
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
+ */
+ public int playSilence(final long durationInMs, final int queueMode,
+ final HashMap<String, String> params, final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
- Uri earconUri = mEarcons.get(earcon);
- if (earconUri == null) {
- return ERROR;
- }
- return service.playAudio(getCallerIdentity(), earconUri, queueMode,
- getParams(params));
+ return service.playSilence(getCallerIdentity(), durationInMs,
+ queueMode, utteranceId);
}
- }, ERROR, "playEarcon");
+ }, ERROR, "playSilence");
}
/**
@@ -998,16 +1167,14 @@
* engine named "com.svox.pico" if it is being used.
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
+ * @deprecated As of API level 20, replaced by
+ * {@link #playSilence(long, int, HashMap, String)}.
*/
+ @Deprecated
public int playSilence(final long durationInMs, final int queueMode,
final HashMap<String, String> params) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- return service.playSilence(getCallerIdentity(), durationInMs, queueMode,
- params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
- }
- }, ERROR, "playSilence");
+ return playSilence(durationInMs, queueMode, params,
+ params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
}
/**
@@ -1283,25 +1450,22 @@
* requests and then returns. The synthesis might not have finished (or even started!) at the
* time when this method returns. In order to reliably detect errors during synthesis,
* we recommend setting an utterance progress listener (see
- * {@link #setOnUtteranceProgressListener}) and using the
- * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+ * {@link #setOnUtteranceProgressListener}).
*
* @param text The text that should be synthesized. No longer than
* {@link #getMaxSpeechInputLength()} characters.
* @param params Parameters for the request. Can be null.
- * Supported parameter names:
- * {@link Engine#KEY_PARAM_UTTERANCE_ID}.
* Engine specific parameters may be passed in but the parameter keys
* must be prefixed by the name of the engine they are intended for. For example
* the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
* engine named "com.svox.pico" if it is being used.
* @param filename Absolute file filename to write the generated audio data to.It should be
* something like "/sdcard/myappsounds/mysound.wav".
- *
+ * @param utteranceId An unique identifier for this request.
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
*/
- public int synthesizeToFile(final String text, final HashMap<String, String> params,
- final String filename) {
+ public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params,
+ final String filename, final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
@@ -1318,7 +1482,7 @@
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
- fileDescriptor, getParams(params));
+ fileDescriptor, getParams(params), utteranceId);
fileDescriptor.close();
return returnValue;
} catch (FileNotFoundException e) {
@@ -1332,10 +1496,41 @@
}, ERROR, "synthesizeToFile");
}
+ /**
+ * Synthesizes the given text to a file using the specified parameters.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
+ *
+ * @param text The text that should be synthesized. No longer than
+ * {@link #getMaxSpeechInputLength()} characters.
+ * @param params Parameters for the request. Can be null.
+ * Supported parameter names:
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
+ * engine named "com.svox.pico" if it is being used.
+ * @param filename Absolute file filename to write the generated audio data to.It should be
+ * something like "/sdcard/myappsounds/mysound.wav".
+ *
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
+ * @deprecated As of API level 20, replaced by
+ * {@link #synthesizeToFile(CharSequence, HashMap, String, String)}.
+ */
+ public int synthesizeToFile(final String text, final HashMap<String, String> params,
+ final String filename) {
+ return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID));
+ }
+
private Bundle getParams(HashMap<String, String> params) {
if (params != null && !params.isEmpty()) {
Bundle bundle = new Bundle(mParams);
copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
+ copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID);
copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a53518f..017be93 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -17,6 +17,8 @@
import android.app.Service;
import android.content.Intent;
+import android.media.AudioManager;
+import android.media.AudioSystem;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -419,6 +421,73 @@
public void dispatchOnError(int errorCode);
}
+
+ /** Set of parameters affecting audio output. */
+ static class AudioOutputParams {
+ /**
+ * Audio session identifier. May be used to associate audio playback with one of the
+ * {@link android.media.audiofx.AudioEffect} objects. If not specified by client,
+ * it should be equal to {@link AudioSystem#AUDIO_SESSION_ALLOCATE}.
+ */
+ public final int mSessionId;
+
+ /**
+ * Audio stream type. Must be one of the STREAM_ contants defined in
+ * {@link android.media.AudioManager}.
+ */
+ public final int mStreamType;
+
+ /**
+ * Volume, in the range [0.0f, 1.0f]. The default value is
+ * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+ */
+ public final float mVolume;
+
+ /**
+ * Left/right position of the audio, in the range [-1.0f, 1.0f].
+ * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+ */
+ public final float mPan;
+
+ /** Create AudioOutputParams with default values */
+ AudioOutputParams() {
+ mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
+ mStreamType = Engine.DEFAULT_STREAM;
+ mVolume = Engine.DEFAULT_VOLUME;
+ mPan = Engine.DEFAULT_PAN;
+ }
+
+ AudioOutputParams(int sessionId, int streamType, float volume, float pan) {
+ mSessionId = sessionId;
+ mStreamType = streamType;
+ mVolume = volume;
+ mPan = pan;
+ }
+
+ /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
+ static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle) {
+ if (paramsBundle == null) {
+ return new AudioOutputParams();
+ }
+
+ return new AudioOutputParams(
+ paramsBundle.getInt(
+ Engine.KEY_PARAM_SESSION_ID,
+ AudioSystem.AUDIO_SESSION_ALLOCATE),
+ paramsBundle.getInt(
+ Engine.KEY_PARAM_STREAM,
+ Engine.DEFAULT_STREAM),
+ paramsBundle.getFloat(
+ Engine.KEY_PARAM_VOLUME,
+ Engine.DEFAULT_VOLUME),
+ paramsBundle.getFloat(
+ Engine.KEY_PARAM_PAN,
+ Engine.DEFAULT_PAN));
+ }
+
+ }
+
+
/**
* An item in the synth thread queue.
*/
@@ -568,11 +637,13 @@
*/
private abstract class SpeechItemV1 extends UtteranceSpeechItem {
protected final Bundle mParams;
+ protected final String mUtteranceId;
SpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
- Bundle params) {
+ Bundle params, String utteranceId) {
super(callerIdentity, callerUid, callerPid);
mParams = params;
+ mUtteranceId = utteranceId;
}
boolean hasLanguage() {
@@ -589,25 +660,17 @@
@Override
public String getUtteranceId() {
- return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null);
+ return mUtteranceId;
}
- int getStreamType() {
- return getIntParam(mParams, Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
- }
-
- float getVolume() {
- return getFloatParam(mParams, Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME);
- }
-
- float getPan() {
- return getFloatParam(mParams, Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN);
+ AudioOutputParams getAudioParams() {
+ return AudioOutputParams.createFromV1ParamsBundle(mParams);
}
}
class SynthesisSpeechItemV1 extends SpeechItemV1 {
// Never null.
- private final String mText;
+ private final CharSequence mText;
private final SynthesisRequest mSynthesisRequest;
private final String[] mDefaultLocale;
// Non null after synthesis has started, and all accesses
@@ -617,8 +680,8 @@
private final int mCallerUid;
public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
- Bundle params, String text) {
- super(callerIdentity, callerUid, callerPid, params);
+ Bundle params, String utteranceId, CharSequence text) {
+ super(callerIdentity, callerUid, callerPid, params, utteranceId);
mText = text;
mCallerUid = callerUid;
mSynthesisRequest = new SynthesisRequest(mText, mParams);
@@ -628,7 +691,7 @@
mPackageName);
}
- public String getText() {
+ public CharSequence getText() {
return mText;
}
@@ -668,7 +731,7 @@
}
protected AbstractSynthesisCallback createSynthesisCallback() {
- return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(),
+ return new PlaybackSynthesisCallback(getAudioParams(),
mAudioPlaybackHandler, this, getCallerIdentity(), mEventLogger, false);
}
@@ -713,8 +776,9 @@
private final FileOutputStream mFileOutputStream;
public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid,
- int callerPid, Bundle params, String text, FileOutputStream fileOutputStream) {
- super(callerIdentity, callerUid, callerPid, params, text);
+ int callerPid, Bundle params, String utteranceId, CharSequence text,
+ FileOutputStream fileOutputStream) {
+ super(callerIdentity, callerUid, callerPid, params, utteranceId, text);
mFileOutputStream = fileOutputStream;
}
@@ -740,10 +804,10 @@
private final AudioPlaybackQueueItem mItem;
public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
- Bundle params, Uri uri) {
- super(callerIdentity, callerUid, callerPid, params);
+ Bundle params, String utteranceId, Uri uri) {
+ super(callerIdentity, callerUid, callerPid, params, utteranceId);
mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(),
- TextToSpeechService.this, uri, getStreamType());
+ TextToSpeechService.this, uri, getAudioParams());
}
@Override
@@ -848,19 +912,20 @@
// they can be used as message objects (which are tested for equality using ==).
private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
@Override
- public int speak(IBinder caller, String text, int queueMode, Bundle params) {
+ public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params,
+ String utteranceId) {
if (!checkNonNull(caller, text, params)) {
return TextToSpeech.ERROR;
}
SpeechItem item = new SynthesisSpeechItemV1(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), params, text);
+ Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text);
return mSynthHandler.enqueueSpeechItem(queueMode, item);
}
@Override
- public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor
- fileDescriptor, Bundle params) {
+ public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor
+ fileDescriptor, Bundle params, String utteranceId) {
if (!checkNonNull(caller, text, fileDescriptor, params)) {
return TextToSpeech.ERROR;
}
@@ -872,19 +937,20 @@
fileDescriptor.detachFd());
SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), params, text,
+ Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text,
new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor));
return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
}
@Override
- public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params) {
+ public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params,
+ String utteranceId) {
if (!checkNonNull(caller, audioUri, params)) {
return TextToSpeech.ERROR;
}
SpeechItem item = new AudioSpeechItemV1(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), params, audioUri);
+ Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri);
return mSynthHandler.enqueueSpeechItem(queueMode, item);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index fb4fd59..4144a75 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -379,7 +379,7 @@
okBottom = fitBottom;
}
} else {
- final boolean moreChars = (j + 1 < spanEnd);
+ final boolean moreChars;
int endPos;
int above, below, top, bottom;
float currentTextWidth;
@@ -391,6 +391,7 @@
top = okTop;
bottom = okBottom;
currentTextWidth = okWidth;
+ moreChars = (j + 1 < spanEnd);
} else if (fit != here) {
endPos = fit;
above = fitAscent;
@@ -398,13 +399,21 @@
top = fitTop;
bottom = fitBottom;
currentTextWidth = fitWidth;
+ moreChars = (j + 1 < spanEnd);
} else {
+ // must make progress, so take next character
endPos = here + 1;
- above = fm.ascent;
- below = fm.descent;
- top = fm.top;
- bottom = fm.bottom;
+ // but to deal properly with clusters
+ // take all zero width characters following that
+ while (endPos < spanEnd && widths[endPos - paraStart] == 0) {
+ endPos++;
+ }
+ above = fmAscent;
+ below = fmDescent;
+ top = fmTop;
+ bottom = fmBottom;
currentTextWidth = widths[here - paraStart];
+ moreChars = (endPos < spanEnd);
}
v = out(source, here, endPos,
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index ba6f1d4..fcc3a40 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -20,6 +20,7 @@
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -221,11 +222,16 @@
return lineEnd(widget, buffer);
}
+ private static boolean isTouchSelecting(boolean isMouse, Spannable buffer) {
+ return isMouse ? Touch.isActivelySelecting(buffer) : isSelecting(buffer);
+ }
+
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int initialScrollX = -1;
int initialScrollY = -1;
final int action = event.getAction();
+ final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
if (action == MotionEvent.ACTION_UP) {
initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -236,19 +242,30 @@
if (widget.isFocused() && !widget.didTouchFocusSelect()) {
if (action == MotionEvent.ACTION_DOWN) {
- if (isSelecting(buffer)) {
- int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-
- buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
-
- // Disallow intercepting of the touch events, so that
- // users can scroll and select at the same time.
- // without this, users would get booted out of select
- // mode once the view detected it needed to scroll.
- widget.getParent().requestDisallowInterceptTouchEvent(true);
- }
+ // Capture the mouse pointer down location to ensure selection starts
+ // right under the mouse (and is not influenced by cursor location).
+ // The code below needs to run for mouse events.
+ // For touch events, the code should run only when selection is active.
+ if (isMouse || isTouchSelecting(isMouse, buffer)) {
+ int offset = widget.getOffsetForPosition(event.getX(), event.getY());
+ buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
+ // Disallow intercepting of the touch events, so that
+ // users can scroll and select at the same time.
+ // without this, users would get booted out of select
+ // mode once the view detected it needed to scroll.
+ widget.getParent().requestDisallowInterceptTouchEvent(true);
+ }
} else if (action == MotionEvent.ACTION_MOVE) {
- if (isSelecting(buffer) && handled) {
+
+ // Cursor can be active at any location in the text while mouse pointer can start
+ // selection from a totally different location. Use LAST_TAP_DOWN span to ensure
+ // text selection will start from mouse pointer location.
+ if (isMouse && Touch.isSelectionStarted(buffer)) {
+ int offset = buffer.getSpanStart(LAST_TAP_DOWN);
+ Selection.setSelection(buffer, offset);
+ }
+
+ if (isTouchSelecting(isMouse, buffer) && handled) {
// Before selecting, make sure we've moved out of the "slop".
// handled will be true, if we're in select mode AND we're
// OUT of the slop
@@ -277,7 +294,7 @@
}
int offset = widget.getOffsetForPosition(event.getX(), event.getY());
- if (isSelecting(buffer)) {
+ if (isTouchSelecting(isMouse, buffer)) {
buffer.removeSpan(LAST_TAP_DOWN);
Selection.extendSelection(buffer, offset);
}
@@ -288,7 +305,6 @@
return true;
}
}
-
return handled;
}
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 9394a0b..fee7377 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -119,12 +119,18 @@
ds = buffer.getSpans(0, buffer.length(), DragState.class);
if (ds.length > 0) {
+ ds[0].mIsSelectionStarted = false;
+
if (ds[0].mFarEnough == false) {
int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
if (Math.abs(event.getX() - ds[0].mX) >= slop ||
Math.abs(event.getY() - ds[0].mY) >= slop) {
ds[0].mFarEnough = true;
+ if (event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+ ds[0].mIsActivelySelecting = true;
+ ds[0].mIsSelectionStarted = true;
+ }
}
}
@@ -135,9 +141,14 @@
MetaKeyKeyListener.META_SHIFT_ON) == 1
|| MetaKeyKeyListener.getMetaState(buffer,
MetaKeyKeyListener.META_SELECTING) != 0;
+
+ if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+ ds[0].mIsActivelySelecting = false;
+ }
+
float dx;
float dy;
- if (cap) {
+ if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
// if we're selecting, we want the scroll to go in
// the direction of the drag
dx = event.getX() - ds[0].mX;
@@ -157,11 +168,13 @@
ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - padding));
ny = Math.max(ny, 0);
-
+
int oldX = widget.getScrollX();
int oldY = widget.getScrollY();
- scrollTo(widget, layout, nx, ny);
+ if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+ scrollTo(widget, layout, nx, ny);
+ }
// If we actually scrolled, then cancel the up action.
if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
@@ -194,6 +207,37 @@
return ds.length > 0 ? ds[0].mScrollY : -1;
}
+ /**
+ * Checks if selection is still active.
+ * This is useful for extending Selection span on buffer.
+ * @param buffer The text buffer.
+ * @return true if buffer has been marked for selection.
+ *
+ * @hide
+ */
+ static boolean isActivelySelecting(Spannable buffer) {
+ DragState[] ds;
+ ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+ return ds.length > 0 && ds[0].mIsActivelySelecting;
+ }
+
+ /**
+ * Checks if selection has begun (are we out of slop?).
+ * Note: DragState.mIsSelectionStarted goes back to false with the very next event.
+ * This is useful for starting Selection span on buffer.
+ * @param buffer The text buffer.
+ * @return true if selection has started on the buffer.
+ *
+ * @hide
+ */
+ static boolean isSelectionStarted(Spannable buffer) {
+ DragState[] ds;
+ ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+ return ds.length > 0 && ds[0].mIsSelectionStarted;
+ }
+
private static class DragState implements NoCopySpan {
public float mX;
public float mY;
@@ -201,6 +245,8 @@
public int mScrollY;
public boolean mFarEnough;
public boolean mUsed;
+ public boolean mIsActivelySelecting;
+ public boolean mIsSelectionStarted;
public DragState(float x, float y, int scrollX, int scrollY) {
mX = x;
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 9d4b720..8f7aefd 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -372,7 +372,7 @@
* Perform a {@link #add(Object)} of all values in <var>array</var>
* @param array The array whose contents are to be retrieved.
*/
- public void putAll(ArraySet<? extends E> array) {
+ public void addAll(ArraySet<? extends E> array) {
final int N = array.mSize;
ensureCapacity(mSize + N);
if (mSize == 0) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 1066430..f41afcf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -431,7 +431,7 @@
/**
* Gets the time when the current frame started.
* <p>
- * This method provides the time in nanoseconds when the frame started being rendered.
+ * This method provides the time in milliseconds when the frame started being rendered.
* The frame time provides a stable time base for synchronizing animations
* and drawing. It should be used instead of {@link SystemClock#uptimeMillis()}
* or {@link System#nanoTime()} for animations and drawing in the UI. Using the frame
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 3670eed..3e7aae0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -46,7 +46,8 @@
void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
- in Rect visibleInsets, boolean reportDraw, in Configuration newConfig);
+ in Rect visibleInsets, in Rect stableInsets, boolean reportDraw,
+ in Configuration newConfig);
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index fa5bd88..0f3f182 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -89,7 +89,7 @@
int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, out Rect outFrame, out Rect outOverscanInsets,
- out Rect outContentInsets, out Rect outVisibleInsets,
+ out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
out Configuration outConfig, out Surface outSurface);
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 7f2defda..ae39b7a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3131,6 +3131,24 @@
return symbolicName != null ? symbolicName : Integer.toString(toolType);
}
+ /**
+ * Checks if a mouse or stylus button (or combination of buttons) is pressed.
+ * @param button Button (or combination of buttons).
+ * @return True if specified buttons are pressed.
+ *
+ * @see #BUTTON_PRIMARY
+ * @see #BUTTON_SECONDARY
+ * @see #BUTTON_TERTIARY
+ * @see #BUTTON_FORWARD
+ * @see #BUTTON_BACK
+ */
+ public final boolean isButtonPressed(int button) {
+ if (button == 0) {
+ return false;
+ }
+ return (getButtonState() & button) == button;
+ }
+
public static final Parcelable.Creator<MotionEvent> CREATOR
= new Parcelable.Creator<MotionEvent>() {
public MotionEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1363a5c..4b53c8e 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
@@ -34,7 +35,7 @@
/**
* @hide
*/
-public final class RenderNodeAnimator extends Animator {
+public class RenderNodeAnimator extends Animator {
// Keep in sync with enum RenderProperty in Animator.h
public static final int TRANSLATION_X = 0;
public static final int TRANSLATION_Y = 1;
@@ -83,16 +84,23 @@
private RenderNode mTarget;
private View mViewTarget;
+ private int mRenderProperty = -1;
+ private float mFinalValue;
private TimeInterpolator mInterpolator;
private boolean mStarted = false;
private boolean mFinished = false;
+ private long mUnscaledDuration = 300;
+ private long mUnscaledStartDelay = 0;
+
public static int mapViewPropertyToRenderProperty(int viewProperty) {
return sViewPropertyAnimatorMap.get(viewProperty);
}
public RenderNodeAnimator(int property, float finalValue) {
+ mRenderProperty = property;
+ mFinalValue = finalValue;
init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
property, finalValue));
}
@@ -156,7 +164,16 @@
mStarted = true;
applyInterpolator();
- mTarget.addAnimator(this);
+ nStart(mNativePtr.get());
+
+ // Alpha is a special snowflake that has the canonical value stored
+ // in mTransformationInfo instead of in RenderNode, so we need to update
+ // it with the final value here.
+ if (mRenderProperty == RenderNodeAnimator.ALPHA) {
+ // Don't need null check because ViewPropertyAnimator's
+ // ctor calls ensureTransformationInfo()
+ mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
+ }
final ArrayList<AnimatorListener> listeners = getListeners();
final int numListeners = listeners == null ? 0 : listeners.size();
@@ -201,6 +218,7 @@
public void setTarget(View view) {
mViewTarget = view;
mTarget = view.mRenderNode;
+ mTarget.addAnimator(this);
}
public void setTarget(Canvas canvas) {
@@ -213,12 +231,12 @@
}
public void setTarget(RenderNode node) {
+ if (mTarget != null) {
+ throw new IllegalStateException("Target already set!");
+ }
mViewTarget = null;
mTarget = node;
- }
-
- public RenderNode getTarget() {
- return mTarget;
+ mTarget.addAnimator(this);
}
public void setStartValue(float startValue) {
@@ -232,12 +250,13 @@
if (startDelay < 0) {
throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
}
- nSetStartDelay(mNativePtr.get(), startDelay);
+ mUnscaledStartDelay = startDelay;
+ nSetStartDelay(mNativePtr.get(), (long) (startDelay * ValueAnimator.getDurationScale()));
}
@Override
public long getStartDelay() {
- return nGetStartDelay(mNativePtr.get());
+ return mUnscaledStartDelay;
}
@Override
@@ -246,13 +265,14 @@
if (duration < 0) {
throw new IllegalArgumentException("duration must be positive; " + duration);
}
- nSetDuration(mNativePtr.get(), duration);
+ mUnscaledDuration = duration;
+ nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
return this;
}
@Override
public long getDuration() {
- return nGetDuration(mNativePtr.get());
+ return mUnscaledDuration;
}
@Override
@@ -307,5 +327,6 @@
private static native long nGetStartDelay(long nativePtr);
private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+ private static native void nStart(long animPtr);
private static native void nCancel(long animPtr);
}
diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java
new file mode 100644
index 0000000..151277a
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimatorCompat.java
@@ -0,0 +1,144 @@
+/*
+ * 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.view;
+
+import android.animation.ValueAnimator;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides compatibility for things like start listeners &
+ * start delays for use by ViewPropertyAnimator and ObjectAnimator
+ * @hide
+ */
+public class RenderNodeAnimatorCompat extends RenderNodeAnimator {
+
+ private long mUnscaledStartDelay = 0;
+ private long mStartDelay = 0;
+ private long mStartTime;
+ private boolean mCanceled;
+
+ public RenderNodeAnimatorCompat(int property, float finalValue) {
+ super(property, finalValue);
+ }
+
+ @Override
+ public void setStartDelay(long startDelay) {
+ mUnscaledStartDelay = startDelay;
+ mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+ }
+
+ @Override
+ public long getStartDelay() {
+ return mUnscaledStartDelay;
+ }
+
+ @Override
+ public void start() {
+ if (mStartDelay <= 0) {
+ doStart();
+ } else {
+ getHelper().addDelayedAnimation(this);
+ }
+ }
+
+ private void doStart() {
+ if (!mCanceled) {
+ super.start();
+ }
+ }
+
+ @Override
+ public void cancel() {
+ mCanceled = true;
+ super.cancel();
+ }
+
+ /**
+ * @return true if the animator was started, false if still delayed
+ */
+ private boolean processDelayed(long frameTimeMs) {
+ if (mCanceled) return true;
+
+ if (mStartTime == 0) {
+ mStartTime = frameTimeMs;
+ } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+ doStart();
+ return true;
+ }
+ return false;
+ }
+
+ private static AnimationHelper getHelper() {
+ AnimationHelper helper = sAnimationHelper.get();
+ if (helper == null) {
+ helper = new AnimationHelper();
+ sAnimationHelper.set(helper);
+ }
+ return helper;
+ }
+
+ private static ThreadLocal<AnimationHelper> sAnimationHelper =
+ new ThreadLocal<AnimationHelper>();
+
+ private static class AnimationHelper implements Runnable {
+
+ private ArrayList<RenderNodeAnimatorCompat> mDelayedAnims = new ArrayList<RenderNodeAnimatorCompat>();
+ private final Choreographer mChoreographer;
+ private boolean mCallbackScheduled;
+
+ public AnimationHelper() {
+ mChoreographer = Choreographer.getInstance();
+ }
+
+ public void addDelayedAnimation(RenderNodeAnimatorCompat animator) {
+ mDelayedAnims.add(animator);
+ scheduleCallback();
+ }
+
+ private void scheduleCallback() {
+ if (!mCallbackScheduled) {
+ mCallbackScheduled = true;
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+ }
+ }
+
+ @Override
+ public void run() {
+ long frameTimeMs = mChoreographer.getFrameTime();
+ mCallbackScheduled = false;
+
+ int end = 0;
+ for (int i = 0; i < mDelayedAnims.size(); i++) {
+ RenderNodeAnimatorCompat animator = mDelayedAnims.get(i);
+ if (!animator.processDelayed(frameTimeMs)) {
+ if (end != i) {
+ mDelayedAnims.set(end, animator);
+ }
+ end++;
+ }
+ }
+ while (mDelayedAnims.size() > end) {
+ mDelayedAnims.remove(mDelayedAnims.size() - 1);
+ }
+
+ if (mDelayedAnims.size() > 0) {
+ scheduleCallback();
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4a2cc1a..a2a4540 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -105,6 +105,7 @@
final Rect mWinFrame = new Rect();
final Rect mOverscanInsets = new Rect();
final Rect mContentInsets = new Rect();
+ final Rect mStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
static final int KEEP_SCREEN_ON_MSG = 1;
@@ -518,7 +519,7 @@
visible ? VISIBLE : GONE,
WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
mWinFrame, mOverscanInsets, mContentInsets,
- mVisibleInsets, mConfiguration, mNewSurface);
+ mVisibleInsets, mStableInsets, mConfiguration, mNewSurface);
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mReportDrawNeeded = true;
}
@@ -653,7 +654,8 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
- Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+ Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+ Configuration newConfig) {
SurfaceView surfaceView = mSurfaceView.get();
if (surfaceView != null) {
if (DEBUG) Log.v(
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d544804..3b2e1d1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7240,9 +7240,6 @@
if (viewRootImpl != null) {
viewRootImpl.setAccessibilityFocus(this, null);
}
- Rect rect = (mAttachInfo != null) ? mAttachInfo.mTmpInvalRect : new Rect();
- getDrawingRect(rect);
- requestRectangleOnScreen(rect, false);
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
return true;
@@ -19808,6 +19805,13 @@
final Rect mVisibleInsets = new Rect();
/**
+ * For windows that are full-screen but using insets to layout inside
+ * of the screen decorations, these are the current insets for the
+ * stable system windows.
+ */
+ final Rect mStableInsets = new Rect();
+
+ /**
* The internal insets given by this window. This value is
* supplied by the client (through
* {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 28e8bd6..36e5996 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6476,6 +6476,20 @@
}
/**
+ * @hide Used internally.
+ */
+ public final void copyMarginsFrom(MarginLayoutParams source) {
+ this.leftMargin = source.leftMargin;
+ this.topMargin = source.topMargin;
+ this.rightMargin = source.rightMargin;
+ this.bottomMargin = source.bottomMargin;
+ this.startMargin = source.startMargin;
+ this.endMargin = source.endMargin;
+
+ this.mMarginFlags = source.mMarginFlags;
+ }
+
+ /**
* Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
* to be done so that the new margins are taken into account. Left and right margins may be
* overriden by {@link android.view.View#requestLayout()} depending on layout direction.
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 3f72b4c..4ca5863 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -338,7 +338,8 @@
* By default, the animator uses the default interpolator for ValueAnimator. Calling this method
* will cause the declared object to be used instead.
*
- * @param interpolator The TimeInterpolator to be used for ensuing property animations.
+ * @param interpolator The TimeInterpolator to be used for ensuing property animations. A value
+ * of <code>null</code> will result in linear interpolation.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 8b4277a..20f5182 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -18,6 +18,8 @@
import android.animation.TimeInterpolator;
import android.view.ViewPropertyAnimator.NameValuesHolder;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
import com.android.internal.view.animation.FallbackLUTInterpolator;
@@ -29,6 +31,8 @@
*/
class ViewPropertyAnimatorRT {
+ private static final Interpolator sLinearInterpolator = new LinearInterpolator();
+
private final View mView;
private RenderNodeAnimator mAnimators[] = new RenderNodeAnimator[RenderNodeAnimator.LAST_VALUE + 1];
@@ -65,6 +69,10 @@
long startDelay = parent.getStartDelay();
long duration = parent.getDuration();
TimeInterpolator interpolator = parent.getInterpolator();
+ if (interpolator == null) {
+ // Documented to be LinearInterpolator in ValueAnimator.setInterpolator
+ interpolator = sLinearInterpolator;
+ }
if (!RenderNodeAnimator.isNativeInterpolator(interpolator)) {
interpolator = new FallbackLUTInterpolator(interpolator, duration);
}
@@ -73,21 +81,14 @@
int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
final float finalValue = holder.mFromValue + holder.mDeltaValue;
- RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
+ RenderNodeAnimator animator = new RenderNodeAnimatorCompat(property, finalValue);
animator.setStartDelay(startDelay);
animator.setDuration(duration);
animator.setInterpolator(interpolator);
animator.setTarget(mView);
animator.start();
- // Alpha is a special snowflake that has the canonical value stored
- // in mTransformationInfo instead of in RenderNode, so we need to update
- // it with the final value here.
- if (property == RenderNodeAnimator.ALPHA) {
- // Don't need null check because ViewPropertyAnimator's
- // ctor calls ensureTransformationInfo()
- parent.mView.mTransformationInfo.mAlpha = finalValue;
- }
+ mAnimators[property] = animator;
}
parent.mPendingAnimations.clear();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3219330..5def940 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,7 +21,6 @@
import android.app.ActivityManagerNative;
import android.content.ClipDescription;
import android.content.ComponentCallbacks;
-import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
@@ -205,7 +204,7 @@
/** Set to true while in performTraversals for detecting when die(true) is called from internal
* callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */
boolean mIsInTraversal;
- boolean mFitSystemWindowsRequested;
+ boolean mApplyInsetsRequested;
boolean mLayoutRequested;
boolean mFirst;
boolean mReportNextDraw;
@@ -257,11 +256,13 @@
final Rect mPendingOverscanInsets = new Rect();
final Rect mPendingVisibleInsets = new Rect();
+ final Rect mPendingStableInsets = new Rect();
final Rect mPendingContentInsets = new Rect();
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
- final Rect mFitSystemWindowsInsets = new Rect();
+ final Rect mDispatchContentInsets = new Rect();
+ final Rect mDispatchStableInsets = new Rect();
final Configuration mLastConfiguration = new Configuration();
final Configuration mPendingConfiguration = new Configuration();
@@ -532,6 +533,7 @@
}
mPendingOverscanInsets.set(0, 0, 0, 0);
mPendingContentInsets.set(mAttachInfo.mContentInsets);
+ mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
@@ -816,7 +818,7 @@
@Override
public void requestFitSystemWindows() {
checkThread();
- mFitSystemWindowsRequested = true;
+ mApplyInsetsRequested = true;
scheduleTraversals();
}
@@ -1161,7 +1163,8 @@
}
void dispatchApplyInsets(View host) {
- mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
+ mDispatchContentInsets.set(mAttachInfo.mContentInsets);
+ mDispatchStableInsets.set(mAttachInfo.mStableInsets);
boolean isRound = false;
if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0
&& mDisplay.getDisplayId() == 0) {
@@ -1171,7 +1174,8 @@
com.android.internal.R.bool.config_windowIsRound);
}
host.dispatchApplyWindowInsets(new WindowInsets(
- mFitSystemWindowsInsets, isRound));
+ mDispatchContentInsets, null /* windowDecorInsets */,
+ mDispatchStableInsets, isRound));
}
private void performTraversals() {
@@ -1310,6 +1314,9 @@
if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
insetsChanged = true;
}
+ if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
+ insetsChanged = true;
+ }
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
@@ -1383,8 +1390,8 @@
& WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
}
- if (mFitSystemWindowsRequested) {
- mFitSystemWindowsRequested = false;
+ if (mApplyInsetsRequested) {
+ mApplyInsetsRequested = false;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
dispatchApplyInsets(host);
if (mLayoutRequested) {
@@ -1469,6 +1476,7 @@
+ " overscan=" + mPendingOverscanInsets.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
+ + " visible=" + mPendingStableInsets.toShortString()
+ " surface=" + mSurface);
if (mPendingConfiguration.seq != 0) {
@@ -1484,6 +1492,8 @@
mAttachInfo.mContentInsets);
final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
mAttachInfo.mVisibleInsets);
+ final boolean stableInsetsChanged = !mPendingStableInsets.equals(
+ mAttachInfo.mStableInsets);
if (contentInsetsChanged) {
if (mWidth > 0 && mHeight > 0 && lp != null &&
((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
@@ -1558,12 +1568,19 @@
// Need to relayout with content insets.
contentInsetsChanged = true;
}
+ if (stableInsetsChanged) {
+ mAttachInfo.mStableInsets.set(mPendingStableInsets);
+ if (DEBUG_LAYOUT) Log.v(TAG, "Decor insets changing to: "
+ + mAttachInfo.mStableInsets);
+ // Need to relayout with content insets.
+ contentInsetsChanged = true;
+ }
if (contentInsetsChanged || mLastSystemUiVisibility !=
- mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested
+ mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
- mFitSystemWindowsRequested = false;
+ mApplyInsetsRequested = false;
dispatchApplyInsets(host);
}
if (visibleInsetsChanged) {
@@ -3065,6 +3082,7 @@
if (mWinFrame.equals(args.arg1)
&& mPendingOverscanInsets.equals(args.arg5)
&& mPendingContentInsets.equals(args.arg2)
+ && mPendingStableInsets.equals(args.arg6)
&& mPendingVisibleInsets.equals(args.arg3)
&& args.arg4 == null) {
break;
@@ -3082,6 +3100,7 @@
mWinFrame.set((Rect) args.arg1);
mPendingOverscanInsets.set((Rect) args.arg5);
mPendingContentInsets.set((Rect) args.arg2);
+ mPendingStableInsets.set((Rect) args.arg6);
mPendingVisibleInsets.set((Rect) args.arg3);
args.recycle();
@@ -5167,7 +5186,7 @@
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
- mPendingConfiguration, mSurface);
+ mPendingStableInsets, mPendingConfiguration, mSurface);
//Log.d(TAG, "<<<<<< BACK FROM relayout");
if (restore) {
params.restore();
@@ -5178,6 +5197,7 @@
mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
+ mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
return relayoutResult;
}
@@ -5446,7 +5466,7 @@
}
public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
- Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+ Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) {
if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
+ " contentInsets=" + contentInsets.toShortString()
+ " visibleInsets=" + visibleInsets.toShortString()
@@ -5465,6 +5485,7 @@
args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig;
args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
+ args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
msg.obj = args;
mHandler.sendMessage(msg);
}
@@ -6292,11 +6313,12 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
- Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+ Rect visibleInsets, Rect stableInsets, boolean reportDraw,
+ Configuration newConfig) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
- visibleInsets, reportDraw, newConfig);
+ visibleInsets, stableInsets, reportDraw, newConfig);
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0a44d23..aa71ed8 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -130,7 +130,20 @@
public static final int PROGRESS_SECONDARY_START = 20000;
/** Highest possible value for the secondary progress */
public static final int PROGRESS_SECONDARY_END = 30000;
-
+
+ /**
+ * The transitionName for the status bar background View when a custom background is used.
+ * @see android.view.Window#setStatusBarColor(int)
+ */
+ public static final String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background";
+
+ /**
+ * The transitionName for the navigation bar background View when a custom background is used.
+ * @see android.view.Window#setNavigationBarColor(int)
+ */
+ public static final String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME =
+ "android:navigation:background";
+
/** The default features enabled */
@SuppressWarnings({"PointlessBitwiseExpression"})
protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) |
@@ -1554,6 +1567,9 @@
* If {@param color} is not opaque, consider setting
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
+ * <p>
+ * The transitionName for the view background will be "android:status:background".
+ * </p>
*/
public abstract void setStatusBarColor(int color);
@@ -1573,6 +1589,9 @@
* If {@param color} is not opaque, consider setting
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
+ * <p>
+ * The transitionName for the view background will be "android:navigation:background".
+ * </p>
*/
public abstract void setNavigationBarColor(int color);
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 1d2f1bf..1832cc3 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -30,13 +30,16 @@
* @see View#onApplyWindowInsets(WindowInsets)
*/
public final class WindowInsets {
+
private Rect mSystemWindowInsets;
private Rect mWindowDecorInsets;
+ private Rect mStableInsets;
private Rect mTempRect;
private boolean mIsRound;
private boolean mSystemWindowInsetsConsumed = false;
private boolean mWindowDecorInsetsConsumed = false;
+ private boolean mStableInsetsConsumed = false;
private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
@@ -49,29 +52,21 @@
public static final WindowInsets CONSUMED;
static {
- CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT);
- CONSUMED.mSystemWindowInsetsConsumed = true;
- CONSUMED.mWindowDecorInsetsConsumed = true;
+ CONSUMED = new WindowInsets(null, null, null, false);
}
/** @hide */
- public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) {
- this(systemWindowInsets, windowDecorInsets, false);
- }
-
- /** @hide */
- public WindowInsets(Rect systemWindowInsets, boolean isRound) {
- this(systemWindowInsets, null, isRound);
- }
-
- /** @hide */
- public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) {
+ public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
+ boolean isRound) {
mSystemWindowInsetsConsumed = systemWindowInsets == null;
mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
mWindowDecorInsetsConsumed = windowDecorInsets == null;
mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets;
+ mStableInsetsConsumed = stableInsets == null;
+ mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : stableInsets;
+
mIsRound = isRound;
}
@@ -83,14 +78,16 @@
public WindowInsets(WindowInsets src) {
mSystemWindowInsets = src.mSystemWindowInsets;
mWindowDecorInsets = src.mWindowDecorInsets;
+ mStableInsets = src.mStableInsets;
mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed;
mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
+ mStableInsetsConsumed = src.mStableInsetsConsumed;
mIsRound = src.mIsRound;
}
/** @hide */
public WindowInsets(Rect systemWindowInsets) {
- this(systemWindowInsets, null);
+ this(systemWindowInsets, null, null, false);
}
/**
@@ -272,7 +269,7 @@
* @hide Pending API
*/
public boolean isConsumed() {
- return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed;
+ return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed;
}
/**
@@ -381,9 +378,57 @@
return result;
}
+ /**
+ * @hide
+ */
+ public int getStableInsetTop() {
+ return mStableInsets.top;
+ }
+
+ /**
+ * @hide
+ */
+ public int getStableInsetLeft() {
+ return mStableInsets.left;
+ }
+
+ /**
+ * @hide
+ */
+ public int getStableInsetRight() {
+ return mStableInsets.right;
+ }
+
+ /**
+ * @hide
+ */
+ public int getStableInsetBottom() {
+ return mStableInsets.bottom;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasStableInsets() {
+ return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
+ || mStableInsets.bottom != 0;
+ }
+
+ /**
+ * @hide
+ */
+ public WindowInsets consumeStableInsets() {
+ final WindowInsets result = new WindowInsets(this);
+ result.mStableInsets = EMPTY_RECT;
+ result.mStableInsetsConsumed = true;
+ return result;
+ }
+
@Override
public String toString() {
- return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets + " windowDecorInsets=" +
- mWindowDecorInsets + (isRound() ? "round}" : "}");
+ return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
+ + " windowDecorInsets=" + mWindowDecorInsets
+ + " stableInsets=" + mStableInsets +
+ (isRound() ? " round}" : "}");
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 024600d..ee542a1 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -149,9 +149,11 @@
* are visible.
* @param decorFrame The decor frame specified by policy specific to this window,
* to use for proper cropping during animation.
+ * @param stableFrame The frame around which stable system decoration is positioned.
*/
public void computeFrameLw(Rect parentFrame, Rect displayFrame,
- Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame);
+ Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
+ Rect stableFrame);
/**
* Retrieve the current frame of the window that has been assigned by
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index d3d5fff..a22931a 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -291,15 +291,30 @@
*/
public Builder setMatrix(final Matrix matrix) {
mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX);
+ mMatrixInitialized = true;
return this;
}
private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
+ private boolean mMatrixInitialized = false;
/**
- * @return {@link CursorAnchorInfo} using parameters in this
- * {@link Builder}.
+ * @return {@link CursorAnchorInfo} using parameters in this {@link Builder}.
+ * @throws IllegalArgumentException if one or more positional parameters are specified but
+ * the coordinate transformation matrix is not provided via {@link #setMatrix(Matrix)}.
*/
public CursorAnchorInfo build() {
+ if (!mMatrixInitialized) {
+ // Coordinate transformation matrix is mandatory when positional parameters are
+ // specified.
+ if ((mCharacterRectBuilder != null && !mCharacterRectBuilder.isEmpty()) ||
+ !Float.isNaN(mInsertionMarkerHorizontal) ||
+ !Float.isNaN(mInsertionMarkerTop) ||
+ !Float.isNaN(mInsertionMarkerBaseline) ||
+ !Float.isNaN(mInsertionMarkerBottom)) {
+ throw new IllegalArgumentException("Coordinate transformation matrix is " +
+ "required when positional parameters are specified.");
+ }
+ }
return new CursorAnchorInfo(this);
}
@@ -317,6 +332,7 @@
mInsertionMarkerBaseline = Float.NaN;
mInsertionMarkerBottom = Float.NaN;
mMatrix.set(Matrix.IDENTITY_MATRIX);
+ mMatrixInitialized = false;
if (mCharacterRectBuilder != null) {
mCharacterRectBuilder.reset();
}
diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java
index 40cade7..d4ec9d0 100644
--- a/core/java/android/view/inputmethod/SparseRectFArray.java
+++ b/core/java/android/view/inputmethod/SparseRectFArray.java
@@ -196,6 +196,10 @@
private float[] mCoordinates = null;
private static int INITIAL_SIZE = 16;
+ public boolean isEmpty() {
+ return mCount <= 0;
+ }
+
/**
* @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}.
*/
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 50cdc0e..8f8876b 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2495,6 +2495,17 @@
}
/**
+ * Positions the selector in a way that mimics touch.
+ */
+ void positionSelectorLikeTouch(int position, View sel, float x, float y) {
+ positionSelectorLikeFocus(position, sel);
+
+ if (mSelector != null && position != INVALID_POSITION) {
+ mSelector.setHotspot(x, y);
+ }
+ }
+
+ /**
* Positions the selector in a way that mimics keyboard focus.
*/
void positionSelectorLikeFocus(int position, View sel) {
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 1fddf3e..7123b9a 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -668,7 +668,8 @@
private SubMenuBuilder mSubMenu;
public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
- super(context, subMenu);
+ super(context, subMenu, null, false,
+ com.android.internal.R.attr.actionOverflowMenuStyle);
mSubMenu = subMenu;
MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index f91865b..84b213b 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1363,7 +1363,13 @@
// Forward converted event to destination view, then recycle it.
final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId);
dstEvent.recycle();
- return handled;
+
+ // Always cancel forwarding when the touch stream ends.
+ final int action = srcEvent.getActionMasked();
+ final boolean keepForwarding = action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL;
+
+ return handled && keepForwarding;
}
private class DisallowIntercept implements Runnable {
@@ -1492,7 +1498,7 @@
}
final View child = getChildAt(position - getFirstVisiblePosition());
- setPressedItem(child, position);
+ setPressedItem(child, position, x, y);
handledEvent = true;
if (actionMasked == MotionEvent.ACTION_UP) {
@@ -1555,7 +1561,7 @@
}
}
- private void setPressedItem(View child, int position) {
+ private void setPressedItem(View child, int position, float x, float y) {
mDrawsInPressedState = true;
// Ordering is essential. First update the pressed state and layout
@@ -1565,7 +1571,7 @@
// Ensure that keyboard focus starts from the last touched position.
setSelectedPositionInt(position);
- positionSelectorLikeFocus(position, child);
+ positionSelectorLikeTouch(position, child, x, y);
// Refresh the drawable state to reflect the new pressed state,
// which will also update the selector state.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8567bed..5743882 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -37,8 +37,6 @@
import android.inputmethodservice.ExtractEditText;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -97,6 +95,7 @@
import android.util.TypedValue;
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.ActionMode;
+import android.view.Choreographer;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -9088,26 +9087,22 @@
}
}
- private static final class Marquee extends Handler {
+ private static final class Marquee {
// TODO: Add an option to configure this
private static final float MARQUEE_DELTA_MAX = 0.07f;
private static final int MARQUEE_DELAY = 1200;
private static final int MARQUEE_RESTART_DELAY = 1200;
- private static final int MARQUEE_RESOLUTION = 1000 / 30;
- private static final int MARQUEE_PIXELS_PER_SECOND = 30;
+ private static final int MARQUEE_DP_PER_SECOND = 30;
private static final byte MARQUEE_STOPPED = 0x0;
private static final byte MARQUEE_STARTING = 0x1;
private static final byte MARQUEE_RUNNING = 0x2;
- private static final int MESSAGE_START = 0x1;
- private static final int MESSAGE_TICK = 0x2;
- private static final int MESSAGE_RESTART = 0x3;
-
private final WeakReference<TextView> mView;
+ private final Choreographer mChoreographer;
private byte mStatus = MARQUEE_STOPPED;
- private final float mScrollUnit;
+ private final float mPixelsPerSecond;
private float mMaxScroll;
private float mMaxFadeScroll;
private float mGhostStart;
@@ -9116,49 +9111,62 @@
private int mRepeatLimit;
private float mScroll;
+ private long mLastAnimationMs;
Marquee(TextView v) {
final float density = v.getContext().getResources().getDisplayMetrics().density;
- mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION;
+ mPixelsPerSecond = MARQUEE_DP_PER_SECOND * density;
mView = new WeakReference<TextView>(v);
+ mChoreographer = Choreographer.getInstance();
}
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_START:
- mStatus = MARQUEE_RUNNING;
- tick();
- break;
- case MESSAGE_TICK:
- tick();
- break;
- case MESSAGE_RESTART:
- if (mStatus == MARQUEE_RUNNING) {
- if (mRepeatLimit >= 0) {
- mRepeatLimit--;
- }
- start(mRepeatLimit);
- }
- break;
+ private Choreographer.FrameCallback mTickCallback = new Choreographer.FrameCallback() {
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ tick();
}
- }
+ };
+
+ private Choreographer.FrameCallback mStartCallback = new Choreographer.FrameCallback() {
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ mStatus = MARQUEE_RUNNING;
+ mLastAnimationMs = mChoreographer.getFrameTime();
+ tick();
+ }
+ };
+
+ private Choreographer.FrameCallback mRestartCallback = new Choreographer.FrameCallback() {
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ if (mStatus == MARQUEE_RUNNING) {
+ if (mRepeatLimit >= 0) {
+ mRepeatLimit--;
+ }
+ start(mRepeatLimit);
+ }
+ }
+ };
void tick() {
if (mStatus != MARQUEE_RUNNING) {
return;
}
- removeMessages(MESSAGE_TICK);
+ mChoreographer.removeFrameCallback(mTickCallback);
final TextView textView = mView.get();
if (textView != null && (textView.isFocused() || textView.isSelected())) {
- mScroll += mScrollUnit;
+ long currentMs = mChoreographer.getFrameTime();
+ long deltaMs = currentMs - mLastAnimationMs;
+ mLastAnimationMs = currentMs;
+ float deltaPx = deltaMs / 1000f * mPixelsPerSecond;
+ mScroll += deltaPx;
if (mScroll > mMaxScroll) {
mScroll = mMaxScroll;
- sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY);
+ mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);
} else {
- sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION);
+ mChoreographer.postFrameCallback(mTickCallback);
}
textView.invalidate();
}
@@ -9166,9 +9174,9 @@
void stop() {
mStatus = MARQUEE_STOPPED;
- removeMessages(MESSAGE_START);
- removeMessages(MESSAGE_RESTART);
- removeMessages(MESSAGE_TICK);
+ mChoreographer.removeFrameCallback(mStartCallback);
+ mChoreographer.removeFrameCallback(mRestartCallback);
+ mChoreographer.removeFrameCallback(mTickCallback);
resetScroll();
}
@@ -9199,7 +9207,7 @@
mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;
textView.invalidate();
- sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY);
+ mChoreographer.postFrameCallback(mStartCallback);
}
}
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 122df2c..24950f6 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1233,7 +1233,7 @@
}
if (layoutSubtitle) {
final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
- titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
+ titleHeight += lp.topMargin + mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin;
}
if (layoutTitle || layoutSubtitle) {
@@ -1585,6 +1585,10 @@
/**
* Layout information for child views of Toolbars.
*
+ * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
+ * ActionBar API. See {@link android.app.Activity#setActionBar(Toolbar) Activity.setActionBar}
+ * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
+ *
* @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
*/
public static class LayoutParams extends ActionBar.LayoutParams {
@@ -1624,6 +1628,9 @@
public LayoutParams(MarginLayoutParams source) {
super(source);
+ // ActionBar.LayoutParams doesn't have a MarginLayoutParams constructor.
+ // Fake it here and copy over the relevant data.
+ copyMarginsFrom(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 7a9137b..ae9b515 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -58,6 +58,8 @@
void noteVibratorOn(int uid, long durationMillis);
void noteVibratorOff(int uid);
+ void noteFlashlightOn();
+ void noteFlashlightOff();
void noteStartGps(int uid);
void noteStopGps(int uid);
void noteScreenState(int state);
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index ec2d654..7acf4d3 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -37,6 +37,8 @@
import java.text.Collator;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
import java.util.ArrayList;
@@ -108,8 +110,9 @@
final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
final Resources resources = context.getResources();
- ArrayList<String> localeList = new ArrayList<String>(Arrays.asList(
- Resources.getSystem().getAssets().getLocales()));
+ String[] locales = Resources.getSystem().getAssets().getLocales();
+ List<String> localeList = new ArrayList<String>(locales.length);
+ Collections.addAll(localeList, locales);
if (isInDeveloperMode) {
if (!localeList.contains("zz_ZZ")) {
localeList.add("zz_ZZ");
@@ -120,78 +123,61 @@
* }
*/
}
- String[] locales = new String[localeList.size()];
- locales = localeList.toArray(locales);
+ Collections.sort(localeList);
final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
- Arrays.sort(locales);
- final int origSize = locales.length;
- final LocaleInfo[] preprocess = new LocaleInfo[origSize];
- int finalSize = 0;
- for (int i = 0 ; i < origSize; i++ ) {
- final String s = locales[i];
- final int len = s.length();
- if (len == 5) {
- String language = s.substring(0, 2);
- String country = s.substring(3, 5);
- final Locale l = new Locale(language, country);
- if (finalSize == 0) {
+ final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
+ for (String locale : localeList) {
+ final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
+ if (l == null || "und".equals(l.getLanguage())) {
+ continue;
+ }
+
+ if (localeInfos.isEmpty()) {
+ if (DEBUG) {
+ Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+ }
+ localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
+ } else {
+ // check previous entry:
+ // same lang and a country -> upgrade to full name and
+ // insert ours with full name
+ // diff lang -> insert ours with lang-only name
+ final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
+ if (previous.locale.getLanguage().equals(l.getLanguage()) &&
+ !previous.locale.getLanguage().equals("zz")) {
if (DEBUG) {
- Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+ Log.v(TAG, "backing up and fixing " + previous.label + " to " +
+ getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
}
- preprocess[finalSize++] =
- new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
+ previous.label = toTitleCase(getDisplayName(
+ previous.locale, specialLocaleCodes, specialLocaleNames));
+ if (DEBUG) {
+ Log.v(TAG, " and adding "+ toTitleCase(
+ getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
+ }
+ localeInfos.add(new LocaleInfo(toTitleCase(
+ getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
} else {
- // check previous entry:
- // same lang and a country -> upgrade to full name and
- // insert ours with full name
- // diff lang -> insert ours with lang-only name
- if (preprocess[finalSize-1].locale.getLanguage().equals(
- language) &&
- !preprocess[finalSize-1].locale.getLanguage().equals("zz")) {
- if (DEBUG) {
- Log.v(TAG, "backing up and fixing "+
- preprocess[finalSize-1].label+" to "+
- getDisplayName(preprocess[finalSize-1].locale,
- specialLocaleCodes, specialLocaleNames));
- }
- preprocess[finalSize-1].label = toTitleCase(
- getDisplayName(preprocess[finalSize-1].locale,
- specialLocaleCodes, specialLocaleNames));
- if (DEBUG) {
- Log.v(TAG, " and adding "+ toTitleCase(
- getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
- }
- preprocess[finalSize++] =
- new LocaleInfo(toTitleCase(
- getDisplayName(
- l, specialLocaleCodes, specialLocaleNames)), l);
+ String displayName;
+ if (locale.equals("zz_ZZ")) {
+ displayName = "[Developer] Accented English";
+ } else if (locale.equals("zz_ZY")) {
+ displayName = "[Developer] Fake Bi-Directional";
} else {
- String displayName;
- if (s.equals("zz_ZZ")) {
- displayName = "[Developer] Accented English";
- } else if (s.equals("zz_ZY")) {
- displayName = "[Developer] Fake Bi-Directional";
- } else {
- displayName = toTitleCase(l.getDisplayLanguage(l));
- }
- if (DEBUG) {
- Log.v(TAG, "adding "+displayName);
- }
- preprocess[finalSize++] = new LocaleInfo(displayName, l);
+ displayName = toTitleCase(l.getDisplayLanguage(l));
}
+ if (DEBUG) {
+ Log.v(TAG, "adding "+displayName);
+ }
+ localeInfos.add(new LocaleInfo(displayName, l));
}
}
}
- final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
- for (int i = 0; i < finalSize; i++) {
- localeInfos[i] = preprocess[i];
- }
- Arrays.sort(localeInfos);
-
+ Collections.sort(localeInfos);
final LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index dab3aff..832829d 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -19,6 +19,7 @@
import android.content.pm.PackageManager;
import android.util.Slog;
+import java.io.Closeable;
import java.io.File;
import java.io.IOException;
@@ -39,20 +40,29 @@
*
* @hide
*/
- public static class ApkHandle {
+ public static class ApkHandle implements Closeable {
final String apkPath;
final long apkHandle;
- public ApkHandle(String path) {
- apkPath = path;
- apkHandle = nativeOpenApk(apkPath);
+ public static ApkHandle create(String path) throws IOException {
+ final long handle = nativeOpenApk(path);
+ if (handle == 0) {
+ throw new IOException("Unable to open APK: " + path);
+ }
+
+ return new ApkHandle(path, handle);
}
- public ApkHandle(File apkFile) {
- apkPath = apkFile.getPath();
- apkHandle = nativeOpenApk(apkPath);
+ public static ApkHandle create(File path) throws IOException {
+ return create(path.getAbsolutePath());
}
+ private ApkHandle(String apkPath, long apkHandle) {
+ this.apkPath = apkPath;
+ this.apkHandle = apkHandle;
+ }
+
+ @Override
public void close() {
nativeClose(apkHandle);
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 6ca24d7..247c8fe 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -54,6 +54,7 @@
PHONE,
WIFI,
BLUETOOTH,
+ FLASHLIGHT,
SCREEN,
APP,
USER,
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 7ff949e..469aa6f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -706,6 +706,15 @@
}
}
+ private void addFlashlightUsage() {
+ long flashlightOnTimeMs = mStats.getFlashlightOnTime(mRawRealtime, mStatsType) / 1000;
+ double flashlightPower = flashlightOnTimeMs
+ * mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT) / (60*60*1000);
+ if (flashlightPower != 0) {
+ addEntry(BatterySipper.DrainType.FLASHLIGHT, flashlightOnTimeMs, flashlightPower);
+ }
+ }
+
private void addUserUsage() {
for (int i=0; i<mUserSippers.size(); i++) {
final int userId = mUserSippers.keyAt(i);
@@ -760,6 +769,7 @@
addUserUsage();
addPhoneUsage();
addScreenUsage();
+ addFlashlightUsage();
addWiFiUsage();
addBluetoothUsage();
addIdleUsage(); // Not including cellular idle power
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 02e4b3f..49d11dc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -89,7 +89,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 107 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 108 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -260,6 +260,9 @@
boolean mVideoOn;
StopwatchTimer mVideoOnTimer;
+ boolean mFlashlightOn;
+ StopwatchTimer mFlashlightOnTimer;
+
int mPhoneSignalStrengthBin = -1;
int mPhoneSignalStrengthBinRaw = -1;
final StopwatchTimer[] mPhoneSignalStrengthsTimer =
@@ -3177,6 +3180,32 @@
getUidStatsLocked(uid).noteVibratorOffLocked();
}
+ public void noteFlashlightOnLocked() {
+ if (!mFlashlightOn) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mFlashlightOn = true;
+ mFlashlightOnTimer.startRunningLocked(elapsedRealtime);
+ }
+ }
+
+ public void noteFlashlightOffLocked() {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ if (mFlashlightOn) {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mFlashlightOn = false;
+ mFlashlightOnTimer.stopRunningLocked(elapsedRealtime);
+ }
+ }
+
public void noteWifiRunningLocked(WorkSource ws) {
if (!mGlobalWifiRunning) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -3682,6 +3711,14 @@
return mBluetoothStateTimer[bluetoothState].getCountLocked(which);
}
+ @Override public long getFlashlightOnTime(long elapsedRealtimeUs, int which) {
+ return mFlashlightOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public long getFlashlightOnCount(int which) {
+ return mFlashlightOnTimer.getCountLocked(which);
+ }
+
@Override
public long getNetworkActivityBytes(int type, int which) {
if (type >= 0 && type < mNetworkByteActivityCounters.length) {
@@ -5681,6 +5718,7 @@
}
mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
+ mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
mOnBattery = mOnBatteryInternal = false;
long uptime = SystemClock.uptimeMillis() * 1000;
long realtime = SystemClock.elapsedRealtime() * 1000;
@@ -5930,6 +5968,7 @@
mPhoneOnTimer.reset(false);
mAudioOnTimer.reset(false);
mVideoOnTimer.reset(false);
+ mFlashlightOnTimer.reset(false);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].reset(false);
}
@@ -7124,6 +7163,8 @@
for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
mBluetoothStateTimer[i].readSummaryFromParcelLocked(in);
}
+ mFlashlightOn = false;
+ mFlashlightOnTimer.readSummaryFromParcelLocked(in);
int NKW = in.readInt();
if (NKW > 10000) {
@@ -7381,6 +7422,7 @@
for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
mBluetoothStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
+ mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -7667,6 +7709,8 @@
mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
mVideoOn = false;
mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
+ mFlashlightOn = false;
+ mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
@@ -7798,6 +7842,7 @@
for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime);
}
+ mFlashlightOnTimer.writeToParcel(out, uSecRealtime);
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
@@ -7931,6 +7976,8 @@
pr.println("*** Bluetooth active type #" + i + ":");
mBluetoothStateTimer[i].logState(pr, " ");
}
+ pr.println("*** Flashlight timer:");
+ mFlashlightOnTimer.logState(pr, " ");
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 94750d3..b3bafa1 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -133,6 +133,11 @@
*/
public static final String POWER_VIDEO = "dsp.video";
+ /**
+ * Power consumption when camera flashlight is on.
+ */
+ public static final String POWER_FLASHLIGHT = "camera.flashlight";
+
public static final String POWER_CPU_SPEEDS = "cpu.speeds";
/**
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 86c9fe3..50a7a5e 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -35,7 +35,7 @@
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
- Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+ Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) {
if (reportDraw) {
try {
mSession.finishDrawing(this);
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
index 30cd11b..8213cb7 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -133,11 +133,12 @@
}
final int contentInsetStart = a.getDimensionPixelOffset(
- R.styleable.ActionBar_contentInsetStart, 0);
+ R.styleable.ActionBar_contentInsetStart, -1);
final int contentInsetEnd = a.getDimensionPixelOffset(
- R.styleable.ActionBar_contentInsetEnd, 0);
- if (contentInsetStart > 0 || contentInsetEnd > 0) {
- mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd);
+ R.styleable.ActionBar_contentInsetEnd, -1);
+ if (contentInsetStart >= 0 || contentInsetEnd >= 0) {
+ mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0),
+ Math.max(contentInsetEnd, 0));
}
final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 4798f58..25d889e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -395,8 +395,8 @@
property_get("ro.product.locale.language", propLang, "en");
property_get("ro.product.locale.region", propRegn, "US");
}
- strncat(language, propLang, 2);
- strncat(region, propRegn, 2);
+ strncat(language, propLang, 3);
+ strncat(region, propRegn, 3);
//ALOGD("language=%s region=%s\n", language, region);
}
@@ -494,6 +494,8 @@
char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold:") + PROPERTY_VALUE_MAX];
char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold:") + PROPERTY_VALUE_MAX];
+ char profile_type[sizeof("-Xprofile-type:") + PROPERTY_VALUE_MAX];
+ char profile_max_stack_depth[sizeof("-Xprofile-max-stack-depth:")+PROPERTY_VALUE_MAX];
char langOption[sizeof("-Duser.language=") + 3];
char regionOption[sizeof("-Duser.region=") + 3];
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
@@ -876,6 +878,20 @@
opt.optionString = profile_top_k_change_threshold;
mOptions.add(opt);
}
+
+ // Type of profile data.
+ strcpy(profile_type, "-Xprofile-type:");
+ if (property_get("dalvik.vm.profiler.type", profile_type+15, NULL) > 0) {
+ opt.optionString = profile_type;
+ mOptions.add(opt);
+ }
+
+ // Depth of bounded stack data
+ strcpy(profile_max_stack_depth, "-Xprofile-max-stack-depth:");
+ if (property_get("dalvik.vm.profile.max-stack-depth", profile_max_stack_depth+26, NULL) > 0) {
+ opt.optionString = profile_max_stack_depth;
+ mOptions.add(opt);
+ }
}
initArgs.version = JNI_VERSION_1_4;
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c139c9d..f7886d3 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,6 +1,7 @@
#include "SkBitmap.h"
#include "SkPixelRef.h"
#include "SkImageEncoder.h"
+#include "SkImageInfo.h"
#include "SkColorPriv.h"
#include "GraphicsJNI.h"
#include "SkDither.h"
@@ -97,13 +98,14 @@
}
// can return NULL
-static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
- switch (config) {
- case SkBitmap::kARGB_8888_Config:
- return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
- case SkBitmap::kARGB_4444_Config:
- return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
- case SkBitmap::kRGB_565_Config:
+static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) {
+ switch (bitmap.colorType()) {
+ case kN32_SkColorType:
+ return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D32 : FromColor_D32_Raw;
+ case kARGB_4444_SkColorType:
+ return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_D4444 :
+ FromColor_D4444_Raw;
+ case kRGB_565_SkColorType:
return FromColor_D565;
default:
break;
@@ -112,11 +114,10 @@
}
bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
- int x, int y, int width, int height,
- const SkBitmap& dstBitmap, bool isPremultiplied) {
+ int x, int y, int width, int height, const SkBitmap& dstBitmap) {
SkAutoLockPixels alp(dstBitmap);
void* dst = dstBitmap.getPixels();
- FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
+ FromColorProc proc = ChooseFromColorProc(dstBitmap);
if (NULL == dst || NULL == proc) {
return false;
@@ -257,22 +258,46 @@
}
// can return NULL
-static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
+static ToColorProc ChooseToColorProc(const SkBitmap& src) {
switch (src.colorType()) {
case kN32_SkColorType:
- if (src.isOpaque()) return ToColor_S32_Opaque;
- return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
+ switch (src.alphaType()) {
+ case kOpaque_SkAlphaType:
+ return ToColor_S32_Opaque;
+ case kPremul_SkAlphaType:
+ return ToColor_S32_Alpha;
+ case kUnpremul_SkAlphaType:
+ return ToColor_S32_Raw;
+ default:
+ return NULL;
+ }
case kARGB_4444_SkColorType:
- if (src.isOpaque()) return ToColor_S4444_Opaque;
- return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
+ switch (src.alphaType()) {
+ case kOpaque_SkAlphaType:
+ return ToColor_S4444_Opaque;
+ case kPremul_SkAlphaType:
+ return ToColor_S4444_Alpha;
+ case kUnpremul_SkAlphaType:
+ return ToColor_S4444_Raw;
+ default:
+ return NULL;
+ }
case kRGB_565_SkColorType:
return ToColor_S565;
case kIndex_8_SkColorType:
if (src.getColorTable() == NULL) {
return NULL;
}
- if (src.isOpaque()) return ToColor_SI8_Opaque;
- return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
+ switch (src.alphaType()) {
+ case kOpaque_SkAlphaType:
+ return ToColor_SI8_Opaque;
+ case kPremul_SkAlphaType:
+ return ToColor_SI8_Alpha;
+ case kUnpremul_SkAlphaType:
+ return ToColor_SI8_Raw;
+ default:
+ return NULL;
+ }
default:
break;
}
@@ -315,7 +340,7 @@
if (jColors != NULL) {
GraphicsJNI::SetPixels(env, jColors, offset, stride,
- 0, 0, width, height, bitmap, true);
+ 0, 0, width, height, bitmap);
}
return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
@@ -477,20 +502,38 @@
return static_cast<jint>(bitmap->getGenerationID());
}
+static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ if (bitmap->alphaType() == kPremul_SkAlphaType) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
}
-static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
- jboolean hasAlpha, jboolean isPremul) {
+static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle,
+ jboolean hasAlpha, jboolean requestPremul) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- if (!hasAlpha) {
- bitmap->setAlphaType(kOpaque_SkAlphaType);
- } else if (isPremul) {
- bitmap->setAlphaType(kPremul_SkAlphaType);
+ if (hasAlpha) {
+ bitmap->setAlphaType(requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
} else {
- bitmap->setAlphaType(kUnpremul_SkAlphaType);
+ bitmap->setAlphaType(kOpaque_SkAlphaType);
+ }
+}
+
+static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
+ jboolean isPremul) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ if (!bitmap->isOpaque()) {
+ if (isPremul) {
+ bitmap->setAlphaType(kPremul_SkAlphaType);
+ } else {
+ bitmap->setAlphaType(kUnpremul_SkAlphaType);
+ }
}
}
@@ -661,11 +704,11 @@
///////////////////////////////////////////////////////////////////////////////
static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
- jint x, jint y, jboolean isPremultiplied) {
+ jint x, jint y) {
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkAutoLockPixels alp(*bitmap);
- ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+ ToColorProc proc = ChooseToColorProc(*bitmap);
if (NULL == proc) {
return 0;
}
@@ -681,11 +724,11 @@
static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
jintArray pixelArray, jint offset, jint stride,
- jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+ jint x, jint y, jint width, jint height) {
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkAutoLockPixels alp(*bitmap);
- ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+ ToColorProc proc = ChooseToColorProc(*bitmap);
if (NULL == proc) {
return;
}
@@ -708,7 +751,7 @@
///////////////////////////////////////////////////////////////////////////////
static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
- jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
+ jint x, jint y, jint colorHandle) {
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkColor color = static_cast<SkColor>(colorHandle);
SkAutoLockPixels alp(*bitmap);
@@ -716,7 +759,7 @@
return;
}
- FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
+ FromColorProc proc = ChooseFromColorProc(*bitmap);
if (NULL == proc) {
return;
}
@@ -727,10 +770,10 @@
static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
jintArray pixelArray, jint offset, jint stride,
- jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+ jint x, jint y, jint width, jint height) {
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
- x, y, width, height, *bitmap, isPremultiplied);
+ x, y, width, height, *bitmap);
}
static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
@@ -834,7 +877,9 @@
{ "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
{ "nativeConfig", "(J)I", (void*)Bitmap_config },
{ "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
- { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
+ { "nativeIsPremultiplied", "(J)Z", (void*)Bitmap_isPremultiplied},
+ { "nativeSetHasAlpha", "(JZZ)V", (void*)Bitmap_setHasAlpha},
+ { "nativeSetPremultiplied", "(JZ)V", (void*)Bitmap_setPremultiplied},
{ "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap },
{ "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap },
{ "nativeCreateFromParcel",
@@ -845,10 +890,10 @@
{ "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;",
(void*)Bitmap_extractAlpha },
{ "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId },
- { "nativeGetPixel", "(JIIZ)I", (void*)Bitmap_getPixel },
- { "nativeGetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
- { "nativeSetPixel", "(JIIIZ)V", (void*)Bitmap_setPixel },
- { "nativeSetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
+ { "nativeGetPixel", "(JII)I", (void*)Bitmap_getPixel },
+ { "nativeGetPixels", "(J[IIIIIII)V", (void*)Bitmap_getPixels },
+ { "nativeSetPixel", "(JIII)V", (void*)Bitmap_setPixel },
+ { "nativeSetPixels", "(J[IIIIIII)V", (void*)Bitmap_setPixels },
{ "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
(void*)Bitmap_copyPixelsToBuffer },
{ "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 3f323ab..6254f3d 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -24,6 +24,7 @@
#include "SkDeque.h"
#include "SkDrawFilter.h"
#include "SkGraphics.h"
+#include <SkImageInfo.h>
#include "SkPorterDuff.h"
#include "SkShader.h"
#include "SkTArray.h"
@@ -699,6 +700,8 @@
jboolean hasAlpha, jlong paintHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+ // correct the alphaType to kOpaque_SkAlphaType.
SkImageInfo info = SkImageInfo::Make(width, height,
hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
kPremul_SkAlphaType);
@@ -708,7 +711,7 @@
}
if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
- 0, 0, width, height, bitmap, true)) {
+ 0, 0, width, height, bitmap)) {
return;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7fda3d9..0ad7fd8 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -90,10 +90,11 @@
/** Copy the colors in colors[] to the bitmap, convert to the correct
format along the way.
+ Whether to use premultiplied pixels is determined by dstBitmap's alphaType.
*/
static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset,
int srcStride, int x, int y, int width, int height,
- const SkBitmap& dstBitmap, bool isPremultiplied);
+ const SkBitmap& dstBitmap);
static jbyteArray getBitmapStorageObj(SkPixelRef *pixref);
};
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 2f24a69..9621bb2 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -25,10 +25,15 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
+#include <gui/Surface.h>
+#include <gui/IGraphicBufferProducer.h>
#include <ui/GraphicBuffer.h>
#include <system/window.h>
#include <hardware/camera3.h>
+#include <stdint.h>
+#include <inttypes.h>
+
using namespace android;
// fully-qualified class name
@@ -192,8 +197,8 @@
switch(pixelFmt) {
case HAL_PIXEL_FORMAT_YCrCb_420_SP: {
if (bufSize < width * height * 4) {
- ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
- bufSize);
+ ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+ __FUNCTION__, bufSize);
return BAD_VALUE;
}
uint8_t* img = NULL;
@@ -214,8 +219,8 @@
}
case HAL_PIXEL_FORMAT_YV12: {
if (bufSize < width * height * 4) {
- ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
- bufSize);
+ ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+ __FUNCTION__, bufSize);
return BAD_VALUE;
}
@@ -251,8 +256,8 @@
// Software writes with YCbCr_420_888 format are unsupported
// by the gralloc module for now
if (bufSize < width * height * 4) {
- ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
- bufSize);
+ ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+ __FUNCTION__, bufSize);
return BAD_VALUE;
}
android_ycbcr ycbcr = android_ycbcr();
@@ -269,7 +274,7 @@
}
case HAL_PIXEL_FORMAT_BLOB: {
if (bufSize != width || height != 1) {
- ALOGE("%s: Incorrect pixelBuffer size: %lld", __FUNCTION__, bufSize);
+ ALOGE("%s: Incorrect pixelBuffer size: %" PRId32, __FUNCTION__, bufSize);
return BAD_VALUE;
}
int8_t* img = NULL;
@@ -316,20 +321,39 @@
if (surface) {
anw = android_view_Surface_getNativeWindow(env, surface);
if (env->ExceptionCheck()) {
- return anw;
+ return NULL;
}
} else {
jniThrowNullPointerException(env, "surface");
- return anw;
+ return NULL;
}
if (anw == NULL) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Surface had no valid native window.");
- return anw;
+ return NULL;
}
return anw;
}
+static sp<Surface> getSurface(JNIEnv* env, jobject surface) {
+ sp<Surface> s;
+ if (surface) {
+ s = android_view_Surface_getSurface(env, surface);
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ } else {
+ jniThrowNullPointerException(env, "surface");
+ return NULL;
+ }
+ if (s == NULL) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Surface had no valid native Surface.");
+ return NULL;
+ }
+ return s;
+}
+
extern "C" {
static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) {
@@ -456,6 +480,30 @@
return NO_ERROR;
}
+static jlong LegacyCameraDevice_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jobject surface) {
+ ALOGV("nativeGetSurfaceId");
+ sp<Surface> s;
+ if ((s = getSurface(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve native Surface from surface.", __FUNCTION__);
+ return 0;
+ }
+ sp<IGraphicBufferProducer> gbp = s->getIGraphicBufferProducer();
+ if (gbp == NULL) {
+ ALOGE("%s: Could not retrieve IGraphicBufferProducer from surface.", __FUNCTION__);
+ return 0;
+ }
+ sp<IBinder> b = gbp->asBinder();
+ if (b == NULL) {
+ ALOGE("%s: Could not retrieve IBinder from surface.", __FUNCTION__);
+ return 0;
+ }
+ /*
+ * FIXME: Use better unique ID for surfaces than native IBinder pointer. Fix also in the camera
+ * service (CameraDeviceClient.h).
+ */
+ return reinterpret_cast<jlong>(b.get());
+}
+
} // extern "C"
static JNINativeMethod gCameraDeviceMethods[] = {
@@ -477,6 +525,9 @@
{ "nativeSetSurfaceDimens",
"(Landroid/view/Surface;II)I",
(void *)LegacyCameraDevice_nativeSetSurfaceDimens },
+ { "nativeGetSurfaceId",
+ "(Landroid/view/Surface;)J",
+ (void *)LegacyCameraDevice_nativeGetSurfaceId },
};
// Get all the required offsets in java class and register native functions
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b38f5f2..c352398 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -30,6 +30,7 @@
#include <SkBitmap.h>
#include <SkCanvas.h>
+#include <SkImageInfo.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkPorterDuff.h>
@@ -376,6 +377,8 @@
static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
jlong rendererPtr, jintArray colors, jint offset, jint stride,
jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
+ // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+ // correct the alphaType to kOpaque_SkAlphaType.
const SkImageInfo info = SkImageInfo::Make(width, height,
hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
kPremul_SkAlphaType);
@@ -385,7 +388,7 @@
return;
}
- if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap, true)) {
+ if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap)) {
delete bitmap;
return;
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 6ba22bf..28473e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -456,7 +456,6 @@
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
renderNode->addAnimator(animator);
- animator->start();
}
#endif // USE_OPENGL_RENDERER
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index de3dd16..ed57979 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -149,6 +149,11 @@
animator->setInterpolator(interpolator);
}
+static void start(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+ BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
+ animator->start();
+}
+
static void cancel(JNIEnv* env, jobject clazz, jlong animatorPtr) {
BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr);
animator->cancel();
@@ -173,6 +178,7 @@
{ "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
{ "nGetStartDelay", "(J)J", (void*) getStartDelay },
{ "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
+ { "nStart", "(J)V", (void*) start },
{ "nCancel", "(J)V", (void*) cancel },
#endif
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7db478d..e675dc7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -99,6 +99,7 @@
<protected-broadcast android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.UUID" />
+ <protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
<protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
<protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
<protected-broadcast android:name="android.bluetooth.device.action.DISAPPEARED" />
@@ -253,6 +254,7 @@
<protected-broadcast
android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
<protected-broadcast android:name="android.net.scoring.SCORE_NETWORKS" />
+ <protected-broadcast android:name="android.net.scoring.SCORER_CHANGED" />
<protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
<protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" />
<protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
@@ -396,6 +398,15 @@
android:label="@string/permlab_receiveWapPush"
android:description="@string/permdesc_receiveWapPush" />
+ <!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
+ or perform processing on them. -->
+ <!-- @hide -->
+ <permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
+ android:permissionGroup="android.permission-group.MESSAGES"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_receiveBluetoothMap"
+ android:description="@string/permdesc_receiveBluetoothMap" />
+
<!-- =============================================================== -->
<!-- Permissions for accessing social info (contacts and social) -->
<!-- =============================================================== -->
diff --git a/core/res/res/anim/button_state_list_anim_material.xml b/core/res/res/anim/button_state_list_anim_material.xml
index 01989a4..9a61548 100644
--- a/core/res/res/anim/button_state_list_anim_material.xml
+++ b/core/res/res/anim/button_state_list_anim_material.xml
@@ -18,16 +18,37 @@
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="@integer/button_pressed_animation_duration"
- android:valueTo="@dimen/button_pressed_z"
+ android:valueTo="@dimen/button_pressed_z_material"
+ android:valueType="floatType"/>
+ <objectAnimator android:propertyName="elevation"
+ android:duration="0"
+ android:valueTo="@dimen/button_elevation_material"
android:valueType="floatType"/>
</set>
</item>
<!-- base state -->
- <item>
+ <item android:state_enabled="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="@integer/button_pressed_animation_duration"
android:valueTo="0"
+ android:startDelay="@integer/button_pressed_animation_delay"
+ android:valueType="floatType"/>
+ <objectAnimator android:propertyName="elevation"
+ android:duration="0"
+ android:valueTo="@dimen/button_elevation_material"
+ android:valueType="floatType" />
+ </set>
+ </item>
+ <item>
+ <set>
+ <objectAnimator android:propertyName="translationZ"
+ android:duration="0"
+ android:valueTo="0"
+ android:valueType="floatType"/>
+ <objectAnimator android:propertyName="elevation"
+ android:duration="0"
+ android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
diff --git a/core/res/res/anim/progress_indeterminate_rotation_material.xml b/core/res/res/anim/progress_indeterminate_rotation_material.xml
new file mode 100644
index 0000000..5d3ba22
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_rotation_material.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="6665"
+ android:interpolator="@android:anim/linear_interpolator"
+ android:propertyName="rotation"
+ android:repeatCount="-1"
+ android:valueFrom="0"
+ android:valueTo="720"
+ android:valueType="floatType" />
\ No newline at end of file
diff --git a/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 171688f..0000000
--- a/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index 879d9c2..0000000
--- a/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 1b648db..0000000
--- a/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index fc51595..0000000
--- a/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index dca86ea..0000000
--- a/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 30d732a..0000000
--- a/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index b135338..0000000
--- a/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 7d29d18..0000000
--- a/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100644
index f235aed..0000000
--- a/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png
deleted file mode 100644
index 01eeefe..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png
deleted file mode 100755
index 7556167..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
index a459089..47cc455 100644
--- a/core/res/res/drawable/btn_borderless_material.xml
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -14,8 +14,13 @@
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
- <item android:id="@id/mask"
- android:drawable="@drawable/btn_mtrl_alpha" />
-</ripple>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="@dimen/control_inset_material"
+ android:insetTop="@dimen/control_inset_material"
+ android:insetBottom="@dimen/control_inset_material"
+ android:insetRight="@dimen/control_inset_material">
+ <ripple android:color="?attr/colorControlHighlight">
+ <item android:id="@id/mask"
+ android:drawable="@drawable/btn_default_mtrl_shape" />
+ </ripple>
+</inset>
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
index 9cee3ab..b04d4fb 100644
--- a/core/res/res/drawable/btn_default_material.xml
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -14,10 +14,12 @@
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
- <item>
- <nine-patch android:src="@drawable/btn_mtrl_alpha"
- android:tint="?attr/colorButtonNormal" />
- </item>
-</ripple>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="@dimen/control_inset_material"
+ android:insetTop="@dimen/control_inset_material"
+ android:insetBottom="@dimen/control_inset_material"
+ android:insetRight="@dimen/control_inset_material">
+ <ripple android:color="?attr/colorControlHighlight">
+ <item android:drawable="@drawable/btn_default_mtrl_shape" />
+ </ripple>
+</inset>
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
new file mode 100644
index 0000000..9235c76
--- /dev/null
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<!-- Used as the canonical button shape. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/control_corner_material" />
+ <solid android:color="?attr/colorButtonNormal" />
+ <padding android:top="@dimen/control_padding_material"
+ android:bottom="@dimen/control_padding_material"
+ android:left="@dimen/control_padding_material"
+ android:right="@dimen/control_padding_material" />
+</shape>
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
index 73fe4d3..a9951e7 100644
--- a/core/res/res/drawable/btn_toggle_material.xml
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -15,16 +15,24 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="4dp"
- android:insetTop="4dp"
- android:insetBottom="4dp"
- android:insetRight="4dp">
+ android:insetLeft="@dimen/control_inset_material"
+ android:insetTop="@dimen/control_inset_material"
+ android:insetBottom="@dimen/control_inset_material"
+ android:insetRight="@dimen/control_inset_material">
<layer-list android:paddingMode="stack">
<item>
<ripple android:color="?attr/colorControlHighlight">
<item>
- <nine-patch android:src="@drawable/btn_toggle_mtrl_alpha"
- android:tint="?attr/colorButtonNormal" />
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:topLeftRadius="@dimen/control_corner_material"
+ android:topRightRadius="@dimen/control_corner_material"/>
+ <solid android:color="?attr/colorButtonNormal" />
+ <padding android:top="@dimen/control_padding_material"
+ android:bottom="@dimen/control_padding_material"
+ android:left="@dimen/control_padding_material"
+ android:right="@dimen/control_padding_material" />
+ </shape>
</item>
</ripple>
</item>
diff --git a/core/res/res/drawable/progress_large_material.xml b/core/res/res/drawable/progress_large_material.xml
index 341d2a9..526f914 100644
--- a/core/res/res/drawable/progress_large_material.xml
+++ b/core/res/res/drawable/progress_large_material.xml
@@ -21,4 +21,8 @@
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
+ <target
+ android:name="root"
+ android:animation="@anim/progress_indeterminate_rotation_material" />
+
</animated-vector>
diff --git a/core/res/res/drawable/progress_medium_material.xml b/core/res/res/drawable/progress_medium_material.xml
index 1f2622c..cc35816 100644
--- a/core/res/res/drawable/progress_medium_material.xml
+++ b/core/res/res/drawable/progress_medium_material.xml
@@ -20,4 +20,8 @@
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
+ <target
+ android:name="root"
+ android:animation="@anim/progress_indeterminate_rotation_material" />
+
</animated-vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_small_material.xml b/core/res/res/drawable/progress_small_material.xml
index 43bc569..c6e4380 100644
--- a/core/res/res/drawable/progress_small_material.xml
+++ b/core/res/res/drawable/progress_small_material.xml
@@ -21,4 +21,8 @@
android:name="progressBar"
android:animation="@anim/progress_indeterminate_material" />
+ <target
+ android:name="root"
+ android:animation="@anim/progress_indeterminate_rotation_material" />
+
</animated-vector>
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
index b1900cb..6e0840c 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
@@ -25,8 +25,6 @@
<group
android:name="root"
- android:scaleX="1.0"
- android:scaleY="1.0"
android:translateX="24.0"
android:translateY="24.0" >
<path
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
index 57a6163..7f1231c 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
@@ -25,8 +25,6 @@
<group
android:name="root"
- android:scaleX="1.0"
- android:scaleY="1.0"
android:translateX="24.0"
android:translateY="24.0" >
<path
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
index ea949b7..58ca101 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
@@ -25,8 +25,6 @@
<group
android:name="root"
- android:scaleX="1.0"
- android:scaleY="1.0"
android:translateX="24.0"
android:translateY="24.0" >
<path
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index b135d4e..11d6dca 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -335,7 +335,6 @@
<item>@drawable/btn_check_to_on_mtrl_013</item>
<item>@drawable/btn_check_to_on_mtrl_014</item>
<item>@drawable/btn_check_to_on_mtrl_015</item>
- <item>@drawable/btn_mtrl_alpha</item>
<item>@drawable/btn_radio_to_off_mtrl_000</item>
<item>@drawable/btn_radio_to_off_mtrl_001</item>
<item>@drawable/btn_radio_to_off_mtrl_002</item>
@@ -402,7 +401,6 @@
<item>@drawable/btn_switch_to_on_mtrl_013</item>
<item>@drawable/btn_switch_to_on_mtrl_014</item>
<item>@drawable/btn_toggle_indicator_mtrl_alpha</item>
- <item>@drawable/btn_toggle_mtrl_alpha</item>
<item>@drawable/expander_close_mtrl_alpha</item>
<item>@drawable/expander_open_mtrl_alpha</item>
<item>@drawable/fastscroll_thumb_mtrl_alpha</item>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 18e4574..bae8e8d 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -58,6 +58,14 @@
<dimen name="floating_window_margin_right">16dp</dimen>
<dimen name="floating_window_margin_bottom">32dp</dimen>
- <!-- Amount of elevation for pressed button state -->
- <dimen name="button_pressed_z">2dp</dimen>
+ <!-- Elevation when button is pressed -->
+ <dimen name="button_elevation_material">1dp</dimen>
+ <!-- Z translation to apply when button is pressed -->
+ <dimen name="button_pressed_z_material">2dp</dimen>
+ <!-- Default insets (outer padding) around controls -->
+ <dimen name="control_inset_material">4dp</dimen>
+ <!-- Default inner padding within controls -->
+ <dimen name="control_padding_material">4dp</dimen>
+ <!-- Default rounded corner for controls -->
+ <dimen name="control_corner_material">2dp</dimen>
</resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index e6748c4..fd61f73 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -20,4 +20,5 @@
<integer name="kg_carousel_angle">75</integer>
<integer name="kg_glowpad_rotation_offset">0</integer>
<integer name="button_pressed_animation_duration">100</integer>
+ <integer name="button_pressed_animation_delay">100</integer>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a3262ed..a71ed47 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -761,6 +761,13 @@
messages sent to you without showing them to you.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_receiveBluetoothMap">receive Bluetooth messages (MAP)</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_receiveBluetoothMap">Allows the app to receive and process Bluetooth MAP
+ messages. This means the app could monitor or delete messages sent to your
+ device without showing them to you.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_getTasks">retrieve running apps</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_getTasks">Allows the app to retrieve information
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 75f905c..b496ead 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -355,35 +355,35 @@
<style name="TextAppearance.StatusBar.Material" />
<style name="TextAppearance.StatusBar.Material.EventContent">
- <item name="android:textColor">#90000000</item>
- <item name="android:textSize">@dimen/notification_text_size</item>
+ <item name="textColor">#90000000</item>
+ <item name="textSize">@dimen/notification_text_size</item>
</style>
<style name="TextAppearance.StatusBar.Material.EventContent.Title">
- <item name="android:textColor">#DD000000</item>
- <item name="android:textSize">@dimen/notification_title_text_size</item>
+ <item name="textColor">#DD000000</item>
+ <item name="textSize">@dimen/notification_title_text_size</item>
</style>
<style name="TextAppearance.StatusBar.Material.EventContent.Line2">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
+ <item name="textSize">@dimen/notification_subtext_size</item>
</style>
<style name="TextAppearance.StatusBar.Material.EventContent.Info">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
+ <item name="textSize">@dimen/notification_subtext_size</item>
</style>
<style name="TextAppearance.StatusBar.Material.EventContent.Time">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
+ <item name="textSize">@dimen/notification_subtext_size</item>
</style>
<style name="TextAppearance.StatusBar.Material.EventContent.Emphasis">
- <item name="android:textColor">#66000000</item>
+ <item name="textColor">#66000000</item>
</style>
<style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
<style name="Widget.StatusBar.Material.ProgressBar.Media">
- <item name="android:progressDrawable">@drawable/notification_material_media_progress</item>
+ <item name="progressDrawable">@drawable/notification_material_media_progress</item>
</style>
<!-- Widget Styles -->
@@ -399,9 +399,7 @@
<item name="textColor">?attr/textColorPrimary</item>
<item name="minHeight">48dip</item>
<item name="minWidth">88dip</item>
-
- <!-- TODO: Turn this back on when we support inset drawable outlines. -->
- <!-- <item name="stateListAnimator">@anim/button_state_list_anim_material</item> -->
+ <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
</style>
<!-- Small bordered ink button -->
@@ -430,7 +428,6 @@
<item name="background">@drawable/btn_toggle_material</item>
<item name="textOn">@string/capital_on</item>
<item name="textOff">@string/capital_off</item>
- <item name="minHeight">48dip</item>
</style>
<style name="Widget.Material.ButtonBar">
@@ -515,7 +512,7 @@
<style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
<style name="Widget.Material.GridView" parent="Widget.GridView">
- <item name="android:listSelector">?attr/selectableItemBackground</item>
+ <item name="listSelector">?attr/selectableItemBackground</item>
</style>
<style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
@@ -687,11 +684,11 @@
</style>
<style name="Widget.Material.Toolbar" parent="Widget.Toolbar">
- <item name="android:navigationButtonStyle">@android:style/Widget.Material.Toolbar.Button.Navigation</item>
+ <item name="navigationButtonStyle">@android:style/Widget.Material.Toolbar.Button.Navigation</item>
</style>
<style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Toolbar.Button.Navigation">
- <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
</style>
<style name="Widget.Material.WebTextView" parent="Widget.WebTextView"/>
diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
index 9252270..33192f2 100644
--- a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
+++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
@@ -59,9 +59,6 @@
final float INSERTION_MARKER_TOP = 100.1f;
final float INSERTION_MARKER_BASELINE = 110.4f;
final float INSERTION_MARKER_BOTOM = 111.0f;
- final int CHAR_INDEX = 32;
- final char CHAR_VALUE = 'X';
- final char DEFAULT_CHAR_VALUE = '!';
Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
@@ -138,6 +135,7 @@
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop());
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline());
assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom());
+ assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
}
@SmallTest
@@ -169,6 +167,54 @@
}
@SmallTest
+ public void testMatrixIsRequired() throws Exception {
+ final int SELECTION_START = 30;
+ final int SELECTION_END = 40;
+ final int COMPOSING_TEXT_START = 32;
+ final String COMPOSING_TEXT = "test";
+ final float INSERTION_MARKER_HORIZONTAL = 10.5f;
+ final float INSERTION_MARKER_TOP = 100.1f;
+ final float INSERTION_MARKER_BASELINE = 110.4f;
+ final float INSERTION_MARKER_BOTOM = 111.0f;
+ Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
+ TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
+
+ final Builder builder = new Builder();
+ // Check twice to make sure if Builder#reset() works as expected.
+ for (int repeatCount = 0; repeatCount < 2; ++repeatCount) {
+ builder.setSelectionRange(SELECTION_START, SELECTION_END)
+ .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT);
+ try {
+ // Should succeed as coordinate transformation matrix is not required if no
+ // positional information is specified.
+ new Builder().build();
+ } catch (IllegalArgumentException ex) {
+ assertTrue(false);
+ }
+
+ builder.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
+ INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM);
+ try {
+ // Coordinate transformation matrix is required if no positional information is
+ // specified.
+ new Builder().build();
+ } catch (IllegalArgumentException ex) {
+ assertTrue(true);
+ }
+
+ builder.setMatrix(TRANSFORM_MATRIX);
+ try {
+ // Should succeed as coordinate transformation matrix is required.
+ new Builder().build();
+ } catch (IllegalArgumentException ex) {
+ assertTrue(false);
+ }
+
+ builder.reset();
+ }
+ }
+
+ @SmallTest
public void testBuilderAdd() throws Exception {
// A negative index should be rejected.
try {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 3857ec0..91f5896 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -141,4 +141,8 @@
<library name="javax.obex"
file="/system/framework/javax.obex.jar"/>
+ <!-- These are the standard packages that are white-listed to always have internet
+ access while in power save mode, even if they aren't in the foreground. -->
+ <allow-in-power-save package="com.android.providers.downloads" />
+
</permissions>
diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf
index cb905bc..1d15caa 100644
--- a/data/fonts/Roboto-Black.ttf
+++ b/data/fonts/Roboto-Black.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BlackItalic.ttf b/data/fonts/Roboto-BlackItalic.ttf
index 3ebdc7d..ea35b0b 100644
--- a/data/fonts/Roboto-BlackItalic.ttf
+++ b/data/fonts/Roboto-BlackItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 68822ca..de790fd 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index aebf8eb..49a41d6 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 2041cbc..345febe 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index aa45340..559eaef 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index a85444f..00ed18d 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
index a3c1a1f..7787a51 100644
--- a/data/fonts/Roboto-Medium.ttf
+++ b/data/fonts/Roboto-Medium.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
index b828205..de787a7 100644
--- a/data/fonts/Roboto-MediumItalic.ttf
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 0e58508..d435eca 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 8779333..164b33d 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index b79cb26..7e00da0 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 3e06c7c..901c708 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index aaf9fe0..3ecb90b 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index d2b611f..986f81c 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf
index d4eb198..6e4e2ea 100644
--- a/data/fonts/RobotoCondensed-Light.ttf
+++ b/data/fonts/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf
index a08f3f4..29ae84b 100644
--- a/data/fonts/RobotoCondensed-LightItalic.ttf
+++ b/data/fonts/RobotoCondensed-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index b9fc49c..4298dea 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/docs/html/preview/images/notifications/ReplyAction.png b/docs/html/preview/images/notifications/ReplyAction.png
new file mode 100644
index 0000000..78fc048
--- /dev/null
+++ b/docs/html/preview/images/notifications/ReplyAction.png
Binary files differ
diff --git a/docs/html/preview/images/notifications/Stack.png b/docs/html/preview/images/notifications/Stack.png
new file mode 100644
index 0000000..c6d76b0
--- /dev/null
+++ b/docs/html/preview/images/notifications/Stack.png
Binary files differ
diff --git a/docs/html/preview/images/notifications/WearBasic.png b/docs/html/preview/images/notifications/WearBasic.png
new file mode 100644
index 0000000..8482cd6
--- /dev/null
+++ b/docs/html/preview/images/notifications/WearBasic.png
Binary files differ
diff --git a/docs/html/preview/images/notifications/expandedtext_combo.png b/docs/html/preview/images/notifications/expandedtext_combo.png
index 1a54033..1bc276f 100644
--- a/docs/html/preview/images/notifications/expandedtext_combo.png
+++ b/docs/html/preview/images/notifications/expandedtext_combo.png
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index ef0a411..9d7ef45 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -59,14 +59,18 @@
private final boolean mIsMutable;
/**
- * Represents whether the Bitmap's content is expected to be pre-multiplied.
+ * Represents whether the Bitmap's content is requested to be pre-multiplied.
* Note that isPremultiplied() does not directly return this value, because
- * isPremultiplied() may never return true for a 565 Bitmap.
+ * isPremultiplied() may never return true for a 565 Bitmap or a bitmap
+ * without alpha.
*
* setPremultiplied() does directly set the value so that setConfig() and
* setPremultiplied() aren't order dependent, despite being setters.
+ *
+ * The native bitmap's premultiplication state is kept up to date by
+ * pushing down this preference for every config change.
*/
- private boolean mIsPremultiplied;
+ private boolean mRequestPremultiplied;
private byte[] mNinePatchChunk; // may be null
private int[] mLayoutBounds; // may be null
@@ -105,7 +109,7 @@
*/
@SuppressWarnings({"UnusedDeclaration"}) // called from JNI
Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
- boolean isMutable, boolean isPremultiplied,
+ boolean isMutable, boolean requestPremultiplied,
byte[] ninePatchChunk, int[] layoutBounds) {
if (nativeBitmap == 0) {
throw new RuntimeException("internal error: native bitmap is 0");
@@ -114,7 +118,7 @@
mWidth = width;
mHeight = height;
mIsMutable = isMutable;
- mIsPremultiplied = isPremultiplied;
+ mRequestPremultiplied = requestPremultiplied;
mBuffer = buffer;
// we delete this in our finalizer
mNativeBitmap = nativeBitmap;
@@ -132,10 +136,10 @@
* width/height values
*/
@SuppressWarnings({"UnusedDeclaration"}) // called from JNI
- void reinit(int width, int height, boolean isPremultiplied) {
+ void reinit(int width, int height, boolean requestPremultiplied) {
mWidth = width;
mHeight = height;
- mIsPremultiplied = isPremultiplied;
+ mRequestPremultiplied = requestPremultiplied;
}
/**
@@ -223,7 +227,7 @@
}
nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length,
- mIsPremultiplied);
+ mRequestPremultiplied);
mWidth = width;
mHeight = height;
}
@@ -561,7 +565,7 @@
checkRecycled("Can't copy a recycled bitmap");
Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable);
if (b != null) {
- b.setAlphaAndPremultiplied(hasAlpha(), mIsPremultiplied);
+ b.setPremultiplied(mRequestPremultiplied);
b.mDensity = mDensity;
}
return b;
@@ -738,7 +742,8 @@
// The new bitmap was created from a known bitmap source so assume that
// they use the same density
bitmap.mDensity = source.mDensity;
- bitmap.setAlphaAndPremultiplied(source.hasAlpha(), source.mIsPremultiplied);
+ bitmap.setHasAlpha(source.hasAlpha());
+ bitmap.setPremultiplied(source.mRequestPremultiplied);
canvas.setBitmap(bitmap);
canvas.drawBitmap(source, srcR, dstR, paint);
@@ -1018,6 +1023,9 @@
* <p>This method always returns false if {@link #getConfig()} is
* {@link Bitmap.Config#RGB_565}.</p>
*
+ * <p>The return value is undefined if {@link #getConfig()} is
+ * {@link Bitmap.Config#ALPHA_8}.</p>
+ *
* <p>This method only returns true if {@link #hasAlpha()} returns true.
* A bitmap with no alpha channel can be used both as a pre-multiplied and
* as a non pre-multiplied bitmap.</p>
@@ -1033,7 +1041,7 @@
* @see BitmapFactory.Options#inPremultiplied
*/
public final boolean isPremultiplied() {
- return mIsPremultiplied && getConfig() != Config.RGB_565 && hasAlpha();
+ return nativeIsPremultiplied(mNativeBitmap);
}
/**
@@ -1057,14 +1065,8 @@
* @see BitmapFactory.Options#inPremultiplied
*/
public final void setPremultiplied(boolean premultiplied) {
- mIsPremultiplied = premultiplied;
- nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha(), premultiplied);
- }
-
- /** Helper function to set both alpha and premultiplied. **/
- private final void setAlphaAndPremultiplied(boolean hasAlpha, boolean premultiplied) {
- mIsPremultiplied = premultiplied;
- nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, premultiplied);
+ mRequestPremultiplied = premultiplied;
+ nativeSetPremultiplied(mNativeBitmap, premultiplied);
}
/** Returns the bitmap's width */
@@ -1225,7 +1227,7 @@
* non-opaque per-pixel alpha values.
*/
public void setHasAlpha(boolean hasAlpha) {
- nativeSetAlphaAndPremultiplied(mNativeBitmap, hasAlpha, mIsPremultiplied);
+ nativeSetHasAlpha(mNativeBitmap, hasAlpha, mRequestPremultiplied);
}
/**
@@ -1299,7 +1301,7 @@
public int getPixel(int x, int y) {
checkRecycled("Can't call getPixel() on a recycled bitmap");
checkPixelAccess(x, y);
- return nativeGetPixel(mNativeBitmap, x, y, mIsPremultiplied);
+ return nativeGetPixel(mNativeBitmap, x, y);
}
/**
@@ -1333,7 +1335,7 @@
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
nativeGetPixels(mNativeBitmap, pixels, offset, stride,
- x, y, width, height, mIsPremultiplied);
+ x, y, width, height);
}
/**
@@ -1413,7 +1415,7 @@
throw new IllegalStateException();
}
checkPixelAccess(x, y);
- nativeSetPixel(mNativeBitmap, x, y, color, mIsPremultiplied);
+ nativeSetPixel(mNativeBitmap, x, y, color);
}
/**
@@ -1450,7 +1452,7 @@
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
nativeSetPixels(mNativeBitmap, pixels, offset, stride,
- x, y, width, height, mIsPremultiplied);
+ x, y, width, height);
}
public static final Parcelable.Creator<Bitmap> CREATOR
@@ -1602,17 +1604,15 @@
private static native int nativeRowBytes(long nativeBitmap);
private static native int nativeConfig(long nativeBitmap);
- private static native int nativeGetPixel(long nativeBitmap, int x, int y,
- boolean isPremultiplied);
+ private static native int nativeGetPixel(long nativeBitmap, int x, int y);
private static native void nativeGetPixels(long nativeBitmap, int[] pixels,
int offset, int stride, int x, int y,
- int width, int height, boolean isPremultiplied);
+ int width, int height);
- private static native void nativeSetPixel(long nativeBitmap, int x, int y,
- int color, boolean isPremultiplied);
+ private static native void nativeSetPixel(long nativeBitmap, int x, int y, int color);
private static native void nativeSetPixels(long nativeBitmap, int[] colors,
int offset, int stride, int x, int y,
- int width, int height, boolean isPremultiplied);
+ int width, int height);
private static native void nativeCopyPixelsToBuffer(long nativeBitmap,
Buffer dst);
private static native void nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src);
@@ -1631,9 +1631,12 @@
private static native void nativePrepareToDraw(long nativeBitmap);
private static native boolean nativeHasAlpha(long nativeBitmap);
- private static native void nativeSetAlphaAndPremultiplied(long nativeBitmap,
- boolean hasAlpha,
- boolean isPremul);
+ private static native boolean nativeIsPremultiplied(long nativeBitmap);
+ private static native void nativeSetPremultiplied(long nativeBitmap,
+ boolean isPremul);
+ private static native void nativeSetHasAlpha(long nativeBitmap,
+ boolean hasAlpha,
+ boolean requestPremul);
private static native boolean nativeHasMipMap(long nativeBitmap);
private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 4a8c122..f3ef48b 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -32,7 +32,8 @@
************************************************************/
BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
- : mFinalValue(finalValue)
+ : mTarget(NULL)
+ , mFinalValue(finalValue)
, mDeltaValue(0)
, mFromValue(0)
, mInterpolator(0)
@@ -81,9 +82,14 @@
mStartDelay = startDelay;
}
-void BaseRenderNodeAnimator::pushStaging(RenderNode* target, TreeInfo& info) {
+void BaseRenderNodeAnimator::attach(RenderNode* target) {
+ mTarget = target;
+ onAttached();
+}
+
+void BaseRenderNodeAnimator::pushStaging(TreeInfo& info) {
if (!mHasStartValue) {
- doSetStartValue(getValue(target));
+ doSetStartValue(getValue(mTarget));
}
if (mStagingPlayState > mPlayState) {
mPlayState = mStagingPlayState;
@@ -109,20 +115,25 @@
}
// No interpolator was set, use the default
if (!mInterpolator) {
- setInterpolator(Interpolator::createDefaultInterpolator());
+ mInterpolator = Interpolator::createDefaultInterpolator();
}
if (mDuration < 0 || mDuration > 50000) {
ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
}
}
-bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
+bool BaseRenderNodeAnimator::animate(TreeInfo& info) {
if (mPlayState < RUNNING) {
return false;
}
+ // If BaseRenderNodeAnimator is handling the delay (not typical), then
+ // because the staging properties reflect the final value, we always need
+ // to call setValue even if the animation isn't yet running or is still
+ // being delayed as we need to override the staging value
if (mStartTime > info.frameTimeMs) {
info.out.hasAnimations |= true;
+ setValue(mTarget, mFromValue);
return false;
}
@@ -136,7 +147,7 @@
}
fraction = mInterpolator->interpolate(fraction);
- setValue(target, mFromValue + (mDeltaValue * fraction));
+ setValue(mTarget, mFromValue + (mDeltaValue * fraction));
if (mPlayState == FINISHED) {
callOnFinishedListener(info);
@@ -188,12 +199,17 @@
, mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {
}
-void RenderPropertyAnimator::onAttached(RenderNode* target) {
+void RenderPropertyAnimator::onAttached() {
if (!mHasStartValue
- && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
- setStartValue((target->stagingProperties().*mPropertyAccess->getter)());
+ && mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
+ setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)());
}
- (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+}
+
+void RenderPropertyAnimator::onStagingPlayStateChanged() {
+ if (mStagingPlayState == RUNNING) {
+ (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
+ }
}
uint32_t RenderPropertyAnimator::dirtyMask() {
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index a981b5a..0dda23f 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -50,12 +50,14 @@
ANDROID_API void setListener(AnimationListener* listener) {
mListener = listener;
}
- ANDROID_API void start() { mStagingPlayState = RUNNING; }
- ANDROID_API void cancel() { mStagingPlayState = FINISHED; }
+ ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
+ ANDROID_API void cancel() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
- virtual void onAttached(RenderNode* target) {}
- virtual void pushStaging(RenderNode* target, TreeInfo& info);
- bool animate(RenderNode* target, TreeInfo& info);
+ void attach(RenderNode* target);
+ virtual void onAttached() {}
+ void detach() { mTarget = 0; }
+ void pushStaging(TreeInfo& info);
+ bool animate(TreeInfo& info);
bool isFinished() { return mPlayState == FINISHED; }
float finalValue() { return mFinalValue; }
@@ -68,15 +70,20 @@
virtual float getValue(RenderNode* target) const = 0;
virtual void setValue(RenderNode* target, float value) = 0;
+ RenderNode* target() { return mTarget; }
void callOnFinishedListener(TreeInfo& info);
+ virtual void onStagingPlayStateChanged() {}
+
enum PlayState {
NOT_STARTED,
RUNNING,
FINISHED,
};
+ RenderNode* mTarget;
+
float mFinalValue;
float mDeltaValue;
float mFromValue;
@@ -92,9 +99,9 @@
sp<AnimationListener> mListener;
private:
- void doSetStartValue(float value);
inline void checkMutable();
- void transitionToRunning(TreeInfo& info);
+ virtual void transitionToRunning(TreeInfo& info);
+ void doSetStartValue(float value);
};
class RenderPropertyAnimator : public BaseRenderNodeAnimator {
@@ -116,13 +123,13 @@
ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue);
- virtual void onAttached(RenderNode* target);
-
ANDROID_API virtual uint32_t dirtyMask();
protected:
virtual float getValue(RenderNode* target) const;
virtual void setValue(RenderNode* target, float value);
+ virtual void onAttached();
+ virtual void onStagingPlayStateChanged();
private:
typedef bool (RenderProperties::*SetFloatProperty)(float value);
diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp
index 6a10cf8..27b0893 100644
--- a/libs/hwui/AnimatorManager.cpp
+++ b/libs/hwui/AnimatorManager.cpp
@@ -25,6 +25,7 @@
using namespace std;
static void unref(BaseRenderNodeAnimator* animator) {
+ animator->detach();
animator->decStrong(0);
}
@@ -39,7 +40,7 @@
void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
animator->incStrong(0);
- animator->onAttached(&mParent);
+ animator->attach(&mParent);
mNewAnimators.push_back(animator.get());
}
@@ -58,7 +59,7 @@
move_all(mNewAnimators, mAnimators);
}
for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
- (*it)->pushStaging(&mParent, info);
+ (*it)->pushStaging(info);
}
}
@@ -68,7 +69,7 @@
: mTarget(target), mInfo(info) {}
bool operator() (BaseRenderNodeAnimator* animator) {
- bool remove = animator->animate(&mTarget, mInfo);
+ bool remove = animator->animate(mInfo);
if (remove) {
animator->decStrong(0);
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 57c66da..d553d10 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -427,24 +427,46 @@
return 0;
}
+ /**
+ * @hide
+ * Used to indicate that when parcelling, the tags should be parcelled through the flattened
+ * formatted string, not through the array of strings.
+ * Keep in sync with frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
+ * see definition of kAudioAttributesMarshallTagFlattenTags
+ */
+ public final static int FLATTEN_TAGS = 0x1;
+ /**
+ * When adding tags for writeToParcel(Parcel, int), add them in the list of flags (| NEW_FLAG)
+ */
+ private final static int ALL_PARCEL_FLAGS = FLATTEN_TAGS;
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mUsage);
dest.writeInt(mContentType);
dest.writeInt(mFlags);
- String[] tagsArray = new String[mTags.size()];
- mTags.toArray(tagsArray);
- dest.writeStringArray(tagsArray);
+ dest.writeInt(flags & ALL_PARCEL_FLAGS);
+ if ((flags & FLATTEN_TAGS) == 0) {
+ String[] tagsArray = new String[mTags.size()];
+ mTags.toArray(tagsArray);
+ dest.writeStringArray(tagsArray);
+ } else if ((flags & FLATTEN_TAGS) == FLATTEN_TAGS) {
+ dest.writeString(mFormattedTags);
+ }
}
private AudioAttributes(Parcel in) {
mUsage = in.readInt();
mContentType = in.readInt();
mFlags = in.readInt();
+ boolean hasFlattenedTags = ((in.readInt() & FLATTEN_TAGS) == FLATTEN_TAGS);
mTags = new HashSet<String>();
- String[] tagsArray = in.readStringArray();
- for (int i = tagsArray.length - 1 ; i >= 0 ; i--) {
- mTags.add(tagsArray[i]);
+ if (hasFlattenedTags) {
+ mTags.add(in.readString());
+ } else {
+ String[] tagsArray = in.readStringArray();
+ for (int i = tagsArray.length - 1 ; i >= 0 ; i--) {
+ mTags.add(tagsArray[i]);
+ }
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c8b51e0..98a575a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -54,6 +54,7 @@
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
private final boolean mUseVolumeKeySounds;
+ private final boolean mUseFixedVolume;
private final Binder mToken = new Binder();
private static String TAG = "AudioManager";
AudioPortEventHandler mAudioPortEventHandler;
@@ -441,6 +442,8 @@
mUseVolumeKeySounds = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useVolumeKeySounds);
mAudioPortEventHandler = new AudioPortEventHandler(this);
+ mUseFixedVolume = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_useFixedVolume);
}
private static IAudioService getService()
@@ -577,6 +580,26 @@
}
/**
+ * Indicates if the device implements a fixed volume policy.
+ * <p>Some devices may not have volume control and may operate at a fixed volume,
+ * and may not enable muting or changing the volume of audio streams.
+ * This method will return true on such devices.
+ * <p>The following APIs have no effect when volume is fixed:
+ * <ul>
+ * <li> {@link #adjustVolume(int, int)}
+ * <li> {@link #adjustSuggestedStreamVolume(int, int, int)}
+ * <li> {@link #adjustStreamVolume(int, int, int)}
+ * <li> {@link #setStreamVolume(int, int, int)}
+ * <li> {@link #setRingerMode(int)}
+ * <li> {@link #setStreamSolo(int, boolean)}
+ * <li> {@link #setStreamMute(int, boolean)}
+ * </ul>
+ */
+ public boolean isVolumeFixed() {
+ return mUseFixedVolume;
+ }
+
+ /**
* Adjusts the volume of a particular stream by one step in a direction.
* <p>
* This method should only be used by applications that replace the platform-wide
@@ -614,7 +637,8 @@
* <p>
* This method should only be used by applications that replace the platform-wide
* management of audio settings or the main telephony application.
- *
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
* @param direction The direction to adjust the volume. One of
* {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
* {@link #ADJUST_SAME}.
@@ -622,6 +646,7 @@
* @see #adjustSuggestedStreamVolume(int, int, int)
* @see #adjustStreamVolume(int, int, int)
* @see #setStreamVolume(int, int, int)
+ * @see #isVolumeFixed()
*/
public void adjustVolume(int direction, int flags) {
IAudioService service = getService();
@@ -644,6 +669,8 @@
* This method should only be used by applications that replace the platform-wide
* management of audio settings or the main telephony application.
*
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
* @param direction The direction to adjust the volume. One of
* {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
* {@link #ADJUST_SAME}.
@@ -653,6 +680,7 @@
* @see #adjustVolume(int, int)
* @see #adjustStreamVolume(int, int, int)
* @see #setStreamVolume(int, int, int)
+ * @see #isVolumeFixed()
*/
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
IAudioService service = getService();
@@ -801,10 +829,12 @@
* Silent mode will mute the volume and will not vibrate. Vibrate mode will
* mute the volume and vibrate. Normal mode will be audible and may vibrate
* according to user settings.
- *
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
* @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL},
* {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}.
* @see #getRingerMode()
+ * @see #isVolumeFixed()
*/
public void setRingerMode(int ringerMode) {
if (!isValidRingerMode(ringerMode)) {
@@ -820,13 +850,15 @@
/**
* Sets the volume index for a particular stream.
- *
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
* @param streamType The stream whose volume index should be set.
* @param index The volume index to set. See
* {@link #getStreamMaxVolume(int)} for the largest valid value.
* @param flags One or more flags.
* @see #getStreamMaxVolume(int)
* @see #getStreamVolume(int)
+ * @see #isVolumeFixed()
*/
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
@@ -919,9 +951,13 @@
* <p>
* For a better user experience, applications MUST unsolo a soloed stream
* in onPause() and solo is again in onResume() if appropriate.
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
*
* @param streamType The stream to be soloed/unsoloed.
* @param state The required solo state: true for solo ON, false for solo OFF
+ *
+ * @see #isVolumeFixed()
*/
public void setStreamSolo(int streamType, boolean state) {
IAudioService service = getService();
@@ -948,9 +984,13 @@
* <p>
* This method should only be used by applications that replace the platform-wide
* management of audio settings or the main telephony application.
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
*
* @param streamType The stream to be muted/unmuted.
* @param state The required mute state: true for mute ON, false for mute OFF
+ *
+ * @see #isVolumeFixed()
*/
public void setStreamMute(int streamType, boolean state) {
IAudioService service = getService();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index d882bc8..628d35b 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -63,6 +63,8 @@
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
+
+import android.telecomm.TelecommManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -2725,17 +2727,13 @@
}
private boolean isInCommunication() {
- boolean isOffhook = false;
+ boolean isInAPhoneCall = false;
- if (mVoiceCapable) {
- try {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- if (phone != null) isOffhook = phone.isOffhook();
- } catch (RemoteException e) {
- Log.w(TAG, "Couldn't connect to phone service", e);
- }
- }
- return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
+ TelecommManager telecommManager =
+ (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
+ isInAPhoneCall = telecommManager.isInAPhoneCall();
+
+ return (isInAPhoneCall || getMode() == AudioManager.MODE_IN_COMMUNICATION);
}
/**
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d1909bc..490a8fd 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1441,6 +1441,36 @@
private native void _setAudioStreamType(int streamtype);
+ // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer.h
+ private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
+ /**
+ * Sets the parameter indicated by key.
+ * @param key key indicates the parameter to be set.
+ * @param value value of the parameter to be set.
+ * @return true if the parameter is set successfully, false otherwise
+ * {@hide}
+ */
+ private native boolean setParameter(int key, Parcel value);
+
+ /**
+ * @hide
+ * CANDIDATE FOR PUBLIC API
+ * Must call this method before prepare() or
+ * prepareAsync() in order for the audio attributes to become effective
+ * thereafter.
+ * @param attributes a non-null set of audio attributes
+ */
+ public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException {
+ if (attributes == null) {
+ final String msg = "Cannot set audio attributes to null";
+ throw new IllegalArgumentException(msg);
+ }
+ Parcel pattributes = Parcel.obtain();
+ attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
+ setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
+ pattributes.recycle();
+ }
+
/**
* Sets the player to be looping or non-looping.
*
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9516bf8..1255276 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1185,6 +1185,7 @@
HashSet<String> existingFiles = new HashSet<String>();
String directory = "/sdcard/DCIM/.thumbnails";
String [] files = (new File(directory)).list();
+ Cursor c = null;
if (files == null)
files = new String[0];
@@ -1194,7 +1195,7 @@
}
try {
- Cursor c = mMediaProvider.query(
+ c = mMediaProvider.query(
mPackageName,
mThumbsUri,
new String [] { "_data" },
@@ -1219,11 +1220,12 @@
}
Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
+ } catch (RemoteException e) {
+ // We will soon be killed...
+ } finally {
if (c != null) {
c.close();
}
- } catch (RemoteException e) {
- // We will soon be killed...
}
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6f42057..4587cf56 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -514,6 +514,25 @@
return (jint) streamtype;
}
+static jboolean
+android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
+{
+ ALOGV("setParameter: key %d", key);
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return false;
+ }
+
+ Parcel *request = parcelForJavaObject(env, java_request);
+ status_t err = mp->setParameter(key, *request);
+ if (err == OK) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
static void
android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
{
@@ -857,6 +876,7 @@
{"_reset", "()V", (void *)android_media_MediaPlayer_reset},
{"_setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
{"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer_getAudioStreamType},
+ {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
{"_setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index 0c65283..75278b5 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014 The Android Open Source Project
* Copyright (c) 2008-2009, Motorola, Inc.
*
* All rights reserved.
@@ -66,6 +67,8 @@
private boolean mGetOperation;
+ private boolean mGetFinalFlag;
+
private HeaderSet mRequestHeader;
private HeaderSet mReplyHeader;
@@ -90,6 +93,7 @@
mOperationDone = false;
mMaxPacketSize = maxSize;
mGetOperation = type;
+ mGetFinalFlag = false;
mPrivateInputOpen = false;
mPrivateOutputOpen = false;
@@ -131,6 +135,15 @@
}
/**
+ * Allows to set flag which will force GET to be always sent as single packet request with
+ * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
+ * require requests to be sent this way.
+ */
+ public void setGetFinalFlag(boolean flag) {
+ mGetFinalFlag = flag;
+ }
+
+ /**
* Sends an ABORT message to the server. By calling this method, the
* corresponding input and output streams will be closed along with this
* object.
@@ -551,15 +564,25 @@
if (mGetOperation) {
if (!mOperationDone) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(0x03);
- }
+ if (!mGetFinalFlag) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(0x03);
+ }
- if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
- mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
- }
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+ }
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ } else {
+ more = sendRequest(0x83);
+
+ if (more) {
+ throw new IOException("FINAL_GET forced but data did not fit into single packet!");
+ }
+
mOperationDone = true;
}
}
@@ -613,7 +636,16 @@
if (mPrivateInput == null) {
mPrivateInput = new PrivateInputStream(this);
}
- sendRequest(0x03);
+
+ if (!mGetFinalFlag) {
+ sendRequest(0x03);
+ } else {
+ sendRequest(0x83);
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ }
return true;
} else if (mOperationDone) {
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
index 2b3066f..51b560a 100644
--- a/obex/javax/obex/HeaderSet.java
+++ b/obex/javax/obex/HeaderSet.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014 The Android Open Source Project
* Copyright (c) 2008-2009, Motorola, Inc.
*
* All rights reserved.
@@ -181,6 +182,8 @@
private String mName; // null terminated Unicode text string
+ private boolean mEmptyName;
+
private String mType; // null terminated ASCII text string
private Long mLength; // 4 byte unsigend integer
@@ -235,6 +238,25 @@
}
/**
+ * Sets flag for special "value" of NAME header which should be empty. This
+ * is not the same as NAME header with empty string in which case it will
+ * have length of 5 bytes. It should be 3 bytes with only header id and
+ * length field.
+ */
+ public void setEmptyNameHeader() {
+ mName = null;
+ mEmptyName = true;
+ }
+
+ /**
+ * Gets flag for special "value" of NAME header which should be empty. See
+ * above.
+ */
+ public boolean getEmptyNameHeader() {
+ return mEmptyName;
+ }
+
+ /**
* Sets the value of the header identifier to the value provided. The type
* of object must correspond to the Java type defined in the description of
* this interface. If <code>null</code> is passed as the
@@ -269,6 +291,7 @@
if ((headerValue != null) && (!(headerValue instanceof String))) {
throw new IllegalArgumentException("Name must be a String");
}
+ mEmptyName = false;
mName = (String)headerValue;
break;
case TYPE:
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 8c12a20..0a06709 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2014 The Android Open Source Project
* Copyright (c) 2008-2009, Motorola, Inc.
*
* All rights reserved.
@@ -387,6 +388,11 @@
if (nullOut) {
headImpl.setHeader(HeaderSet.NAME, null);
}
+ } else if (headImpl.getEmptyNameHeader()) {
+ out.write((byte) HeaderSet.NAME);
+ lengthArray[0] = (byte) 0x00;
+ lengthArray[1] = (byte) 0x03;
+ out.write(lengthArray);
}
// Type Header
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 52db30a..168fb41 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -50,6 +50,7 @@
import com.android.internal.app.IMediaContainerService;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.NativeLibraryHelper.ApkHandle;
import com.android.internal.content.PackageHelper;
import java.io.BufferedInputStream;
@@ -107,8 +108,27 @@
return null;
}
- return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
- isExternal, isForwardLocked, abiOverride);
+
+ if (isExternal) {
+ // Make sure the sdcard is mounted.
+ String status = Environment.getExternalStorageState();
+ if (!status.equals(Environment.MEDIA_MOUNTED)) {
+ Slog.w(TAG, "Make sure sdcard is mounted.");
+ return null;
+ }
+ }
+
+ ApkHandle handle = null;
+ try {
+ handle = ApkHandle.create(packageURI.getPath());
+ return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName,
+ isExternal, isForwardLocked, handle, abiOverride);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Problem opening APK: " + packageURI.getPath());
+ return null;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
}
/**
@@ -329,21 +349,11 @@
private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName,
String publicResFileName, boolean isExternal, boolean isForwardLocked,
- String abiOverride) {
-
- if (isExternal) {
- // Make sure the sdcard is mounted.
- String status = Environment.getExternalStorageState();
- if (!status.equals(Environment.MEDIA_MOUNTED)) {
- Slog.w(TAG, "Make sure sdcard is mounted.");
- return null;
- }
- }
-
+ ApkHandle handle, String abiOverride) {
// The .apk file
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
- NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
+
String[] abiList = Build.SUPPORTED_ABIS;
if (abiOverride != null) {
abiList = new String[] { abiOverride };
@@ -850,14 +860,14 @@
private int calculateContainerSize(File apkFile, boolean forwardLocked,
String abiOverride) throws IOException {
- NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile);
- final int abi = NativeLibraryHelper.findSupportedAbi(handle,
- (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
-
+ ApkHandle handle = null;
try {
+ handle = ApkHandle.create(apkFile);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle,
+ (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
return calculateContainerSize(handle, apkFile, abi, forwardLocked);
} finally {
- handle.close();
+ IoUtils.closeQuietly(handle);
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e12549a..ad27e41 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -106,6 +106,8 @@
<!-- Wifi Display -->
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+ <uses-permission android:name="android.permission.CAMERA" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
new file mode 100644
index 0000000..49eba22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.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="64.0dp"
+ android:height="64.0dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#4DFFFFFF"
+ android:pathData="M3.3,3.0L2.0,4.3l5.0,5.0L7.0,13.0l3.0,0.0l0.0,9.0l3.6,-6.1l4.1,4.1l1.3,-1.3L3.3,3.0zM17.0,10.0l-4.0,0.0l4.0,-8.0L7.0,2.0l0.0,2.2l8.5,8.5L17.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
new file mode 100644
index 0000000..101ca84
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.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="64.0dp"
+ android:height="64.0dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M7.0,2.0l0.0,11.0 3.0,0.0 0.0,9.0 7.0,-12.0 -4.0,0.0 4.0,-8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2b7793d..2188a8e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -62,6 +62,14 @@
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/close_handle_underlap"/>
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+ android:layout_gravity="end"
+ android:layout="@layout/keyguard_user_switcher" />
+
<com.android.systemui.statusbar.phone.ObservableScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
@@ -92,14 +100,6 @@
<include layout="@layout/status_bar_expanded_header" />
- <ViewStub
- android:id="@+id/keyguard_user_switcher"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
- android:layout_gravity="end"
- android:layout="@layout/keyguard_user_switcher" />
-
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f021253..a8799f7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -540,6 +540,8 @@
<string name="quick_settings_hotspot_label">Hotspot</string>
<!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
<string name="quick_settings_notifications_label">Notifications</string>
+ <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_flashlight_label">Flashlight</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">No recent apps</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 322174a..85f58881 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -881,8 +881,7 @@
return;
}
- if (!mUserManager.isUserSwitcherEnabled()
- && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
+ if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0720704..d152887 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -50,6 +50,7 @@
private int mPanelPaddingBottom;
private int mDualTileUnderlap;
private boolean mExpanded;
+ private boolean mListening;
private TileRecord mDetailRecord;
private Callback mCallback;
@@ -100,9 +101,14 @@
if (!mExpanded) {
showDetail(false /*show*/, mDetailRecord);
}
+ }
+
+ public void setListening(boolean listening) {
+ if (mListening == listening) return;
+ mListening = listening;
for (TileRecord r : mRecords) {
- r.tile.setListening(mExpanded);
- if (mExpanded) {
+ r.tile.setListening(mListening);
+ if (mListening) {
r.tile.refreshState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 786cd9e..ba350e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -30,6 +30,7 @@
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.Listenable;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -221,6 +222,7 @@
TetheringController getTetheringController();
CastController getCastController();
VolumeComponent getVolumeComponent();
+ FlashlightController getFlashlightController();
}
public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
new file mode 100644
index 0000000..b610cf3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tiles;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.policy.FlashlightController;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings.Secure;
+
+/** Quick settings tile: Control flashlight **/
+public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
+ FlashlightController.FlashlightListener {
+
+ private final FlashlightController mFlashlightController;
+
+ public FlashlightTile(Host host) {
+ super(host);
+ mFlashlightController = host.getFlashlightController();
+ mFlashlightController.addListener(this);
+ }
+
+ @Override
+ protected BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ }
+
+ @Override
+ protected void handleClick() {
+ boolean newState = !mState.value;
+ mFlashlightController.setFlashlight(newState);
+ refreshState(newState);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ if (arg instanceof Boolean) {
+ state.value = (Boolean) arg;
+ }
+ state.visible = mFlashlightController.isAvailable();
+ state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
+ state.iconId = state.value
+ ? R.drawable.ic_qs_flashlight_on : R.drawable.ic_qs_flashlight_off;
+ }
+
+ @Override
+ public void onFlashlightOff() {
+ refreshState(false);
+ }
+
+ @Override
+ public void onFlashlightError() {
+ refreshState(false);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 95edd1c..4c52b24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -147,7 +147,9 @@
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
+ mFullScreenOverlayView, t));
// Call our callback
onEnterAnimationTriggered();
}
@@ -392,7 +394,9 @@
void onConfigurationChange() {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenOverlayView));
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
+ mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
+ mFullScreenOverlayView, t));
// Call our callback
onEnterAnimationTriggered();
}
@@ -536,6 +540,12 @@
final boolean backward = event.isShiftPressed();
mRecentsView.focusNextTask(!backward);
return true;
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+ mRecentsView.focusNextTask(true);
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+ mRecentsView.focusNextTask(false);
+ return true;
}
// Pass through the debug trigger
mDebugTrigger.onKeyEvent(keyCode);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index e7eac67..b5b9cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -186,6 +186,10 @@
/** Requests all task stacks to start their enter-recents animation */
public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.increment();
+
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
@@ -194,6 +198,10 @@
stackView.startEnterRecentsAnimation(ctx);
}
}
+
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.decrement();
}
/** Requests all task stacks to start their exit-recents animation */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 29eaa9e..2c05daa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -747,9 +747,6 @@
// Mark that we have completely the first layout
mAwaitingFirstLayout = false;
- // Start dozing
- mUIDozeTrigger.startDozing();
-
// Prepare the first view for its enter animation
int offsetTopAlign = -mStackAlgorithm.mTaskRect.top;
int offscreenY = mStackAlgorithm.mRect.bottom -
@@ -770,7 +767,11 @@
// Update the focused task index to be the next item to the top task
if (mConfig.launchedWithAltTab) {
+ // When alt-tabbing, we focus the next previous task
focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
+ } else {
+ // Normally we just focus the front task
+ focusTask(Math.max(0, mStack.getTaskCount() - 1), false);
}
}
}
@@ -799,6 +800,15 @@
mStack.indexOfTask(tv.getTask()), getStackScroll());
tv.startEnterRecentsAnimation(ctx);
}
+
+ // Add a runnable to the post animation ref counter to clear all the views
+ ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // Start dozing
+ mUIDozeTrigger.startDozing();
+ }
+ });
}
/** Requests this task stacks to start it's exit-recents animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b1fc700..c6bacbd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -296,7 +298,7 @@
}
/** Animates this task view as it enters recents */
- public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
+ public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
TaskViewTransform transform = ctx.transform;
if (mConfig.launchedFromAppWithScreenshot) {
@@ -308,6 +310,8 @@
// Animate the task bar of the first task view
mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
setVisibility(View.VISIBLE);
+ // Decrement the post animation trigger
+ ctx.postAnimationTrigger.decrement();
}
});
} else {
@@ -321,9 +325,17 @@
.setInterpolator(mConfig.linearOutSlowInInterpolator)
.setDuration(475)
.withLayer()
- .withEndAction(mEnableThumbnailClip)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mEnableThumbnailClip.run();
+ // Decrement the post animation trigger
+ ctx.postAnimationTrigger.decrement();
+ }
+ })
.start();
}
+ ctx.postAnimationTrigger.increment();
} else if (mConfig.launchedFromAppWithThumbnail) {
if (ctx.isFrontMost) {
@@ -335,7 +347,15 @@
anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
anim.setDuration(mConfig.taskBarEnterAnimDuration);
anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Decrement the post animation trigger
+ ctx.postAnimationTrigger.decrement();
+ }
+ });
anim.start();
+ ctx.postAnimationTrigger.increment();
} else {
mEnableThumbnailClip.run();
}
@@ -355,8 +375,16 @@
.setInterpolator(mConfig.quintOutInterpolator)
.setDuration(mConfig.taskViewEnterFromHomeDuration)
.withLayer()
- .withEndAction(mEnableThumbnailClip)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mEnableThumbnailClip.run();
+ // Decrement the post animation trigger
+ ctx.postAnimationTrigger.decrement();
+ }
+ })
.start();
+ ctx.postAnimationTrigger.increment();
}
}
@@ -518,7 +546,12 @@
*/
public void setFocusedTask() {
mIsFocused = true;
+ // Workaround, we don't always want it focusable in touch mode, but we want the first task
+ // to be focused after the enter-recents animation, which can be triggered from either touch
+ // or keyboard
+ setFocusableInTouchMode(true);
requestFocus();
+ setFocusableInTouchMode(false);
invalidate();
mCb.onTaskFocused(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index 13407aa..fa97d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -38,9 +38,13 @@
int stackViewCount;
// Whether this is the front most task view
boolean isFrontMost;
+ // A trigger to run some logic when all the animations complete. This works around the fact
+ // that it is difficult to coordinate ViewPropertyAnimators
+ ReferenceCountedTrigger postAnimationTrigger;
- public TaskViewEnterContext(FullscreenTransitionOverlayView fss) {
+ public TaskViewEnterContext(FullscreenTransitionOverlayView fss, ReferenceCountedTrigger t) {
fullScreenshot = fss;
+ postAnimationTrigger = t;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c82bdf6..b2e79c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -35,6 +35,7 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
@@ -61,7 +62,7 @@
private KeyguardPageSwipeHelper mPageSwiper;
private StatusBarHeaderView mHeader;
private View mQsContainer;
- private View mQsPanel;
+ private QSPanel mQsPanel;
private View mKeyguardStatusView;
private ObservableScrollView mScrollView;
private TextView mClockView;
@@ -77,6 +78,12 @@
private boolean mQsTracking;
/**
+ * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
+ * the expansion for quick settings.
+ */
+ private boolean mConflictingQsExpansionGesture;
+
+ /**
* Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
* intercepted yet.
*/
@@ -94,6 +101,7 @@
private int mQsMaxExpansionHeight;
private int mQsPeekHeight;
private boolean mStackScrollerOverscrolling;
+ private boolean mQsExpansionFromOverscroll;
private boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
@@ -136,7 +144,7 @@
mHeader.setOverlayParent(this);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mQsContainer = findViewById(R.id.quick_settings_container);
- mQsPanel = findViewById(R.id.quick_settings_panel);
+ mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
mClockView = (TextView) findViewById(R.id.clock_view);
mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
mScrollView.setListener(this);
@@ -422,11 +430,21 @@
private void flingQsWithCurrentVelocity() {
float vel = getCurrentVelocity();
-
- // TODO: Better logic whether we should expand or not.
- flingSettings(vel, vel > 0);
+ flingSettings(vel, flingExpandsQs(vel));
}
+ private boolean flingExpandsQs(float vel) {
+ if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ return getQsExpansionFraction() > 0.5f;
+ } else {
+ return vel > 0;
+ }
+ }
+
+ private float getQsExpansionFraction() {
+ return (mQsExpansionHeight - mQsMinExpansionHeight)
+ / (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+ }
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mBlockTouches) {
@@ -442,20 +460,48 @@
return true;
}
}
- if (mQsTracking || mQsExpanded) {
- return onQsTouch(event);
- }
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
+ && mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+ // Down in the empty area while fully expanded - go to QS.
+ mQsTracking = true;
+ mConflictingQsExpansionGesture = true;
+ onQsExpansionStarted();
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = event.getX();
+ mInitialTouchX = event.getY();
+ }
+ if (mQsTracking || mQsExpanded) {
+ onQsTouch(event);
+ if (!mConflictingQsExpansionGesture) {
+ return true;
+ }
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mConflictingQsExpansionGesture = false;
+ }
super.onTouchEvent(event);
return true;
}
@Override
+ protected boolean flingExpands(float vel, float vectorVel) {
+ boolean expands = super.flingExpands(vel, vectorVel);
+
+ // If we are already running a QS expansion, make sure that we keep the panel open.
+ if (mQsExpansionAnimator != null) {
+ expands = true;
+ }
+ return expands;
+ }
+
+ @Override
protected boolean hasConflictingGestures() {
return mStatusBar.getBarState() != StatusBarState.SHADE;
}
- private boolean onQsTouch(MotionEvent event) {
+ private void onQsTouch(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -500,14 +546,17 @@
mQsTracking = false;
mTrackingPointer = -1;
trackMovement(event);
- flingQsWithCurrentVelocity();
+ float fraction = getQsExpansionFraction();
+ if ((fraction != 0f || y >= mInitialTouchY)
+ && (fraction != 1f || y <= mInitialTouchY)) {
+ flingQsWithCurrentVelocity();
+ }
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
- return true;
}
@Override
@@ -523,19 +572,25 @@
@Override
- public void onOverscrollTopChanged(float amount) {
+ public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
cancelAnimation();
float rounded = amount >= 1f ? amount : 0f;
- mStackScrollerOverscrolling = rounded != 0f;
+ mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
+ mQsExpansionFromOverscroll = rounded != 0f;
setQsExpansion(mQsMinExpansionHeight + rounded);
updateQsState();
}
@Override
public void flingTopOverscroll(float velocity, boolean open) {
- mStackScrollerOverscrolling = false;
setQsExpansion(mQsExpansionHeight);
- flingSettings(velocity, open);
+ flingSettings(velocity, open, new Runnable() {
+ @Override
+ public void run() {
+ mStackScrollerOverscrolling = false;
+ mQsExpansionFromOverscroll = false;
+ }
+ });
}
private void onQsExpansionStarted() {
@@ -571,13 +626,12 @@
private void updateQsState() {
boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling;
mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
- mNotificationStackScroller.setEnabled(!mQsExpanded);
+ mNotificationStackScroller.setEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
- mQsContainer.setVisibility(mKeyguardShowing && !expandVisually
- ? View.INVISIBLE
- : View.VISIBLE);
+ mQsContainer.setVisibility(
+ mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
mScrollView.setTouchEnabled(mQsExpanded);
- mNotificationStackScroller.setTouchEnabled(!mQsExpanded);
+ mNotificationStackScroller.setTouchEnabled(!mQsExpanded || mQsExpansionFromOverscroll);
}
private void setQsExpansion(float height) {
@@ -640,9 +694,17 @@
mQsExpansionAnimator.cancel();
}
}
+
private void flingSettings(float vel, boolean expand) {
+ flingSettings(vel, expand, null);
+ }
+
+ private void flingSettings(float vel, boolean expand, final Runnable onFinishRunnable) {
float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
if (target == mQsExpansionHeight) {
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
return;
}
ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
@@ -657,6 +719,9 @@
@Override
public void onAnimationEnd(Animator animation) {
mQsExpansionAnimator = null;
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
+ }
}
});
animator.start();
@@ -873,10 +938,27 @@
super.onExpandingFinished();
mNotificationStackScroller.onExpansionStopped();
mIsExpanding = false;
+ if (mExpandedHeight == 0f) {
+ mHeader.setListening(false);
+ mQsPanel.setListening(false);
+ } else {
+ mHeader.setListening(true);
+ mQsPanel.setListening(true);
+ }
+ }
+
+ @Override
+ public void instantExpand() {
+ super.instantExpand();
+ mHeader.setListening(true);
+ mQsPanel.setListening(true);
}
@Override
protected void setOverExpansion(float overExpansion, boolean isPixels) {
+ if (mConflictingQsExpansionGesture) {
+ return;
+ }
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
mNotificationStackScroller.setOnHeightChangedListener(null);
if (isPixels) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 5b47749..7d5d99d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -301,6 +301,8 @@
mTrackingPointer = -1;
trackMovement(event);
if ((mTracking && mTouchSlopExceeded)
+ || Math.abs(x - mInitialTouchX) > mTouchSlop
+ || Math.abs(y - mInitialTouchY) > mTouchSlop
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
float vel = 0f;
float vectorVel = 0f;
@@ -317,6 +319,7 @@
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
+
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -447,7 +450,7 @@
* @param vectorVel the length of the vectorial velocity
* @return whether a fling should expands the panel; contracts otherwise
*/
- private boolean flingExpands(float vel, float vectorVel) {
+ protected boolean flingExpands(float vel, float vectorVel) {
if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
return getExpandedFraction() > 0.5f;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f70010e..387f5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -31,7 +31,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
@@ -123,6 +122,7 @@
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -194,6 +194,7 @@
CastControllerImpl mCastController;
VolumeComponent mVolumeComponent;
KeyguardUserSwitcher mKeyguardUserSwitcher;
+ FlashlightController mFlashlightController;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -518,7 +519,7 @@
Resources res = context.getResources();
updateDisplaySize(); // populates mDisplayMetrics
- loadDimens();
+ updateResources();
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
@@ -711,6 +712,7 @@
}
mBatteryController.setStatusBarHeaderView(mHeader);
+ mFlashlightController = new FlashlightController(mContext);
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
@@ -726,7 +728,7 @@
final QSTileHost qsh = new QSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, null /*tethering*/,
- mCastController, mVolumeComponent);
+ mCastController, mVolumeComponent, mFlashlightController);
for (QSTile<?> tile : qsh.getTiles()) {
mQSPanel.addTile(tile);
}
@@ -3183,7 +3185,7 @@
}
final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
mVisibleNotifications.clear();
- mVisibleNotifications.putAll(mNewVisibleNotifications);
+ mVisibleNotifications.addAll(mNewVisibleNotifications);
// We have new notifications
if (updates && mDozeServiceHost != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7c87580..60f38b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -29,6 +29,7 @@
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.LocationTile;
import com.android.systemui.qs.tiles.NotificationsTile;
import com.android.systemui.qs.tiles.RotationLockTile;
@@ -37,6 +38,7 @@
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -64,12 +66,13 @@
private final VolumeComponent mVolume;
private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
private final int mFeedbackStartDelay;
+ private final FlashlightController mFlashlight;
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
RotationLockController rotation, NetworkController network,
ZenModeController zen, TetheringController tethering,
- CastController cast, VolumeComponent volume) {
+ CastController cast, VolumeComponent volume, FlashlightController flashlight) {
mContext = context;
mStatusBar = statusBar;
mBluetooth = bluetooth;
@@ -80,6 +83,7 @@
mTethering = tethering;
mCast = cast;
mVolume = volume;
+ mFlashlight = flashlight;
final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
ht.start();
@@ -95,6 +99,7 @@
mTiles.add(new LocationTile(this));
mTiles.add(new CastTile(this));
mTiles.add(new HotspotTile(this));
+ mTiles.add(new FlashlightTile(this));
mUserTracker = new CurrentUserTracker(mContext) {
@Override
@@ -177,4 +182,9 @@
public VolumeComponent getVolumeComponent() {
return mVolume;
}
+
+ @Override
+ public FlashlightController getFlashlightController() {
+ return mFlashlight;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 9e9de5d..3e2dcef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -45,6 +45,7 @@
private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
private boolean mExpanded;
+ private boolean mListening;
private boolean mOverscrolled;
private boolean mKeyguardShowing;
@@ -145,6 +146,14 @@
return mExpandedHeight;
}
+ public void setListening(boolean listening) {
+ if (listening == mListening) {
+ return;
+ }
+ mListening = listening;
+ updateBrightnessControllerState();
+ }
+
public void setExpanded(boolean expanded, boolean overscrolled) {
boolean changed = expanded != mExpanded;
boolean overscrollChanged = overscrolled != mOverscrolled;
@@ -154,7 +163,6 @@
updateHeights();
updateVisibilities();
updateSystemIconsLayoutParams();
- updateBrightnessControllerState();
updateZTranslation();
updateClickTargets();
updateWidth();
@@ -254,7 +262,7 @@
}
private void updateBrightnessControllerState() {
- if (mExpanded) {
+ if (mListening) {
mBrightnessController.registerCallbacks();
} else {
mBrightnessController.unregisterCallbacks();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
new file mode 100644
index 0000000..b059043
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -0,0 +1,308 @@
+/*
+ * 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 android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Manages the flashlight.
+ */
+public class FlashlightController {
+
+ private static final String TAG = "FlashlightController";
+
+ private final CameraManager mCameraManager;
+ /** Call {@link #ensureHandler()} before using */
+ private Handler mHandler;
+
+ /** Lock on mListeners when accessing */
+ private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
+
+ /** Lock on {@code this} when accessing */
+ private boolean mFlashlightEnabled;
+
+ private CameraDevice mCameraDevice;
+ private CaptureRequest mFlashlightRequest;
+ private CameraCaptureSession mSession;
+ private SurfaceTexture mSurfaceTexture;
+ private Surface mSurface;
+
+ public FlashlightController(Context mContext) {
+ mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ }
+
+ public synchronized void setFlashlight(boolean enabled) {
+ if (mFlashlightEnabled != enabled) {
+ mFlashlightEnabled = enabled;
+ postUpdateFlashlight();
+ }
+ }
+
+ public boolean isAvailable() {
+ try {
+ return getCameraId() != null;
+ } catch (CameraAccessException e) {
+ return false;
+ }
+ }
+
+ public void addListener(FlashlightListener l) {
+ synchronized (mListeners) {
+ cleanUpListenersLocked(l);
+ mListeners.add(new WeakReference<>(l));
+ }
+ }
+
+ public void removeListener(FlashlightListener l) {
+ synchronized (mListeners) {
+ cleanUpListenersLocked(l);
+ }
+ }
+
+ private synchronized void ensureHandler() {
+ if (mHandler == null) {
+ HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ }
+ }
+
+ private void startDevice() throws CameraAccessException {
+ mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
+ }
+
+ private void startSession() throws CameraAccessException {
+ mSurfaceTexture = new SurfaceTexture(false);
+ Size size = getSmallestSize(mCameraDevice.getId());
+ mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
+ mSurface = new Surface(mSurfaceTexture);
+ ArrayList<Surface> outputs = new ArrayList<>(1);
+ outputs.add(mSurface);
+ mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
+ }
+
+ private Size getSmallestSize(String cameraId) throws CameraAccessException {
+ Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
+ .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+ .getOutputSizes(SurfaceTexture.class);
+ if (outputSizes == null || outputSizes.length == 0) {
+ throw new IllegalStateException(
+ "Camera " + cameraId + "doesn't support any outputSize.");
+ }
+ Size chosen = outputSizes[0];
+ for (Size s : outputSizes) {
+ if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
+ chosen = s;
+ }
+ }
+ return chosen;
+ }
+
+ private void postUpdateFlashlight() {
+ ensureHandler();
+ mHandler.post(mUpdateFlashlightRunnable);
+ }
+
+ private String getCameraId() throws CameraAccessException {
+ String[] ids = mCameraManager.getCameraIdList();
+ for (String id : ids) {
+ CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
+ Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+ Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
+ if (flashAvailable != null && flashAvailable
+ && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+ return id;
+ }
+ }
+ return null;
+ }
+
+ private void updateFlashlight(boolean forceDisable) {
+ try {
+ boolean enabled;
+ synchronized (this) {
+ enabled = mFlashlightEnabled && !forceDisable;
+ }
+ if (enabled) {
+ if (mCameraDevice == null) {
+ startDevice();
+ return;
+ }
+ if (mSession == null) {
+ startSession();
+ return;
+ }
+ if (mFlashlightRequest == null) {
+ CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
+ CameraDevice.TEMPLATE_PREVIEW);
+ builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
+ builder.addTarget(mSurface);
+ CaptureRequest request = builder.build();
+ mSession.capture(request, null, mHandler);
+ mFlashlightRequest = request;
+ }
+ } else {
+ if (mCameraDevice != null) {
+ mCameraDevice.close();
+ teardown();
+ }
+ }
+
+ } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
+ Log.e(TAG, "Error in updateFlashlight", e);
+ handleError();
+ }
+ }
+
+ private void teardown() {
+ mCameraDevice = null;
+ mSession = null;
+ mFlashlightRequest = null;
+ if (mSurface != null) {
+ mSurface.release();
+ mSurfaceTexture.release();
+ }
+ mSurface = null;
+ mSurfaceTexture = null;
+ }
+
+ private void handleError() {
+ synchronized (this) {
+ mFlashlightEnabled = false;
+ }
+ dispatchError();
+ dispatchOff();
+ updateFlashlight(true /* forceDisable */);
+ }
+
+ private void dispatchOff() {
+ dispatchListeners(false, true /* off */);
+ }
+
+ private void dispatchError() {
+ dispatchListeners(true /* error */, false);
+ }
+
+ private void dispatchListeners(boolean error, boolean off) {
+ synchronized (mListeners) {
+ final int N = mListeners.size();
+ boolean cleanup = false;
+ for (int i = 0; i < N; i++) {
+ FlashlightListener l = mListeners.get(i).get();
+ if (l != null) {
+ if (error) {
+ l.onFlashlightError();
+ } else if (off) {
+ l.onFlashlightOff();
+ }
+ } else {
+ cleanup = true;
+ }
+ }
+ if (cleanup) {
+ cleanUpListenersLocked(null);
+ }
+ }
+ }
+
+ private void cleanUpListenersLocked(FlashlightListener listener) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ FlashlightListener found = mListeners.get(i).get();
+ if (found == null || found == listener) {
+ mListeners.remove(i);
+ }
+ }
+ }
+
+ private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
+ @Override
+ public void onOpened(CameraDevice camera) {
+ mCameraDevice = camera;
+ postUpdateFlashlight();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ if (mCameraDevice == camera) {
+ dispatchOff();
+ teardown();
+ }
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
+ if (camera == mCameraDevice || mCameraDevice == null) {
+ handleError();
+ }
+ }
+ };
+
+ private final CameraCaptureSession.StateListener mSessionListener =
+ new CameraCaptureSession.StateListener() {
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mSession = session;
+ postUpdateFlashlight();
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ Log.e(TAG, "Configure failed.");
+ if (mSession == null || mSession == session) {
+ handleError();
+ }
+ }
+ };
+
+ private final Runnable mUpdateFlashlightRunnable = new Runnable() {
+ @Override
+ public void run() {
+ updateFlashlight(false /* forceDisable */);
+ }
+ };
+
+ public interface FlashlightListener {
+
+ /**
+ * Called when the flashlight turns off unexpectedly.
+ */
+ void onFlashlightOff();
+
+ /**
+ * Called when there is an error that turns the flashlight off.
+ */
+ void onFlashlightError();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c9a1b28..94fdd1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -988,34 +988,50 @@
*/
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators) {
+ setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
+ }
+
+ /**
+ * Set the effective overScroll amount which will be directly reflected in the layout.
+ *
+ * @param amount The amount to overScroll by.
+ * @param onTop Should the effect be applied on top of the scroller.
+ * @param animate Should an animation be performed.
+ * @param cancelAnimators Should running animations be cancelled.
+ * @param isRubberbanded The value which will be passed to
+ * {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
+ */
+ public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+ boolean cancelAnimators, boolean isRubberbanded) {
if (cancelAnimators) {
mStateAnimator.cancelOverScrollAnimators(onTop);
}
- setOverScrollAmountInternal(amount, onTop, animate);
+ setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
}
- private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+ private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
+ boolean isRubberbanded) {
amount = Math.max(0, amount);
if (animate) {
- mStateAnimator.animateOverScrollToAmount(amount, onTop);
+ mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
} else {
setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
if (onTop) {
- notifyOverscrollTopListener(amount);
+ notifyOverscrollTopListener(amount, isRubberbanded);
}
requestChildrenUpdate();
}
}
- private void notifyOverscrollTopListener(float amount) {
+ private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
mExpandHelper.onlyObserveMovements(amount > 1.0f);
if (mDontReportNextOverScroll) {
mDontReportNextOverScroll = false;
return;
}
if (mOverscrollTopChangedListener != null) {
- mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
+ mOverscrollTopChangedListener.onOverscrollTopChanged(amount, isRubberbanded);
}
}
@@ -1061,9 +1077,9 @@
updateChildren();
float overScrollTop = getCurrentOverScrollAmount(true);
if (mOwnScrollY < 0) {
- notifyOverscrollTopListener(-mOwnScrollY);
+ notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
} else {
- notifyOverscrollTopListener(overScrollTop);
+ notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
}
}
} else {
@@ -1288,6 +1304,16 @@
return RUBBER_BAND_FACTOR_NORMAL;
}
+ /**
+ * Accompanying function for {@link #getRubberBandFactor}: Returns true if the overscroll is
+ * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
+ * overscroll view (e.g. expand QS).
+ */
+ private boolean isRubberbanded(boolean onTop) {
+ return !onTop || mExpandedInThisMotion || mIsExpansionChanging
+ || !mScrolledToTopOnFirstDown;
+ }
+
private void endDrag() {
setIsBeingDragged(false);
@@ -1880,7 +1906,16 @@
* A listener that gets notified when the overscroll at the top has changed.
*/
public interface OnOverscrollTopChangedListener {
- public void onOverscrollTopChanged(float amount);
+
+ /**
+ * Notifies a listener that the overscroll has changed.
+ *
+ * @param amount the amount of overscroll, in pixels
+ * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+ * unrubberbanded motion to directly expand overscroll view (e.g expand
+ * QS)
+ */
+ public void onOverscrollTopChanged(float amount, boolean isRubberbanded);
/**
* Notify a listener that the scroller wants to escape from the scrolling motion and
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 494195e..f732cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -719,7 +719,8 @@
}
}
- public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+ public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
+ final boolean isRubberbanded) {
final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
if (targetAmount == startOverScrollAmount) {
return;
@@ -733,7 +734,8 @@
public void onAnimationUpdate(ValueAnimator animation) {
float currentOverScroll = (float) animation.getAnimatedValue();
mHostLayout.setOverScrollAmount(
- currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */);
+ currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
+ isRubberbanded);
}
});
overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 149f4aa..adfa1f2 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -32,20 +32,15 @@
import com.android.internal.view.menu.MenuDialogHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
-import com.android.internal.widget.ActionBarContainer;
import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.ActionBarOverlayLayout;
-import com.android.internal.widget.ActionBarView;
import com.android.internal.widget.DecorContentParent;
import com.android.internal.widget.SwipeDismissLayout;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -122,6 +117,11 @@
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
+ private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
+ (1 << FEATURE_CUSTOM_TITLE) |
+ (1 << FEATURE_CONTENT_TRANSITIONS) |
+ (1 << FEATURE_ACTION_MODE_OVERLAY);
+
/**
* Simple callback used by the context menu and its submenus. The options
* menu submenus do not use this (their behavior is more complex).
@@ -275,16 +275,13 @@
throw new AndroidRuntimeException("requestFeature() must be called before adding content");
}
final int features = getFeatures();
- if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) {
-
- /* Another feature is enabled and the user is trying to enable the custom title feature */
- throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
- }
- if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) &&
- (featureId != FEATURE_CUSTOM_TITLE) && (featureId != FEATURE_ACTION_MODE_OVERLAY)) {
-
- /* Custom title feature is enabled and the user is trying to enable another feature */
- throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
+ final int newFeatures = features | (1 << featureId);
+ if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 &&
+ (newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) {
+ // Another feature is enabled and the user is trying to enable the custom title feature
+ // or custom title feature is enabled and the user is trying to enable another feature
+ throw new AndroidRuntimeException(
+ "You cannot combine custom titles with other title features");
}
if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
return false; // Ignore. No title dominates.
@@ -2647,25 +2644,29 @@
return false;
}
- private void updateColorViews(WindowInsets insets) {
- if (mIsFloating || !ActivityManager.isHighEndGfx()) {
- // No colors on floating windows or low end devices :(
- return;
+ private WindowInsets updateColorViews(WindowInsets insets) {
+ if (!mIsFloating && ActivityManager.isHighEndGfx()) {
+ if (insets != null) {
+ mLastTopInset = insets.getStableInsetTop();
+ mLastBottomInset = insets.getStableInsetBottom();
+ }
+ mStatusColorView = updateColorViewInt(mStatusColorView,
+ SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+ mStatusBarColor, mLastTopInset, Gravity.TOP,
+ STATUS_BAR_BACKGROUND_TRANSITION_NAME);
+ mNavigationColorView = updateColorViewInt(mNavigationColorView,
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+ mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM,
+ NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
}
if (insets != null) {
- mLastTopInset = insets.getSystemWindowInsetTop();
- mLastBottomInset = insets.getSystemWindowInsetBottom();
+ insets = insets.consumeStableInsets();
}
- mStatusColorView = updateColorViewInt(mStatusColorView,
- SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
- mStatusBarColor, mLastTopInset, Gravity.TOP);
- mNavigationColorView = updateColorViewInt(mNavigationColorView,
- SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
- mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM);
+ return insets;
}
private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag,
- int color, int height, int verticalGravity) {
+ int color, int height, int verticalGravity, String transitionName) {
boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0
&& (getAttributes().flags & translucentFlag) == 0
&& (color & Color.BLACK) != 0
@@ -2675,6 +2676,7 @@
if (show) {
view = new View(mContext);
view.setBackgroundColor(color);
+ view.setTransitionName(transitionName);
addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height,
Gravity.START | verticalGravity));
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ddfa9c1..4458a8d 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -64,7 +64,7 @@
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
-import android.telephony.TelephonyManager;
+import android.telecomm.TelecommManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -409,6 +409,7 @@
static final Rect mTmpContentFrame = new Rect();
static final Rect mTmpVisibleFrame = new Rect();
static final Rect mTmpDecorFrame = new Rect();
+ static final Rect mTmpStableFrame = new Rect();
static final Rect mTmpNavigationFrame = new Rect();
WindowState mTopFullscreenOpaqueWindowState;
@@ -1364,6 +1365,7 @@
// XXX right now the app process has complete control over
// this... should introduce a token to let the system
// monitor/control what they are doing.
+ outAppOp[0] = AppOpsManager.OP_TOAST_WINDOW;
break;
case TYPE_DREAM:
case TYPE_INPUT_METHOD:
@@ -2038,8 +2040,8 @@
ServiceManager.checkService(DreamService.DREAM_SERVICE));
}
- TelephonyManager getTelephonyService() {
- return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ TelecommManager getTelecommService() {
+ return (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
}
static IAudioService getAudioService() {
@@ -2126,10 +2128,10 @@
}
// If an incoming call is ringing, HOME is totally disabled.
- // (The user is already on the InCallScreen at this point,
+ // (The user is already on the InCallUI at this point,
// and his ONLY options are to answer or reject the call.)
- TelephonyManager telephonyManager = getTelephonyService();
- if (telephonyManager != null && telephonyManager.isRinging()) {
+ TelecommManager telecommManager = getTelecommService();
+ if (telecommManager != null && telecommManager.isRinging()) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
return -1;
}
@@ -2380,9 +2382,7 @@
if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
if (mRecentAppsHeldModifiers == 0 && !keyguardOn) {
final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
- if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
- || KeyEvent.metaStateHasModifiers(
- shiftlessModifiers, KeyEvent.META_META_ON)) {
+ if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
mRecentAppsHeldModifiers = shiftlessModifiers;
showRecentApps(true);
return -1;
@@ -2975,7 +2975,8 @@
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf);
+ mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+ mTmpNavigationFrame);
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
if (mNavigationBarController.checkHiddenLw()) {
updateSysUiVisibility = true;
@@ -3000,7 +3001,7 @@
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf);
+ mStatusBar.computeFrameLw(pf, df, vf, vf, vf, dcf, vf);
// For layout, the status bar is always at the top with our fixed height.
mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
@@ -3160,6 +3161,7 @@
final Rect cf = mTmpContentFrame;
final Rect vf = mTmpVisibleFrame;
final Rect dcf = mTmpDecorFrame;
+ final Rect sf = mTmpStableFrame;
dcf.setEmpty();
final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
@@ -3167,6 +3169,12 @@
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
+ if (isDefaultDisplay) {
+ sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
+ } else {
+ sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
+ }
+
if (!isDefaultDisplay) {
if (attached != null) {
// If this window is attached to another, our display
@@ -3529,9 +3537,10 @@
+ " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ " of=" + of.toShortString()
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
- + " dcf=" + dcf.toShortString());
+ + " dcf=" + dcf.toShortString()
+ + " sf=" + sf.toShortString());
- win.computeFrameLw(pf, df, of, cf, vf, dcf);
+ win.computeFrameLw(pf, df, of, cf, vf, dcf, sf);
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
@@ -4152,9 +4161,9 @@
}
}
if (down) {
- TelephonyManager telephonyManager = getTelephonyService();
- if (telephonyManager != null) {
- if (telephonyManager.isRinging()) {
+ TelecommManager telecommManager = getTelecommService();
+ if (telecommManager != null) {
+ if (telecommManager.isRinging()) {
// If an incoming call is ringing, either VOLUME key means
// "silence ringer". We handle these keys here, rather than
// in the InCallScreen, to make sure we'll respond to them
@@ -4166,14 +4175,14 @@
// Silence the ringer. (It's safe to call this
// even if the ringer has already been silenced.)
- telephonyManager.silenceRinger();
+ telecommManager.silenceRinger();
// And *don't* pass this key thru to the current activity
// (which is probably the InCallScreen.)
result &= ~ACTION_PASS_TO_USER;
break;
}
- if (telephonyManager.isOffhook()
+ if (telecommManager.isInAPhoneCall()
&& (result & ACTION_PASS_TO_USER) == 0) {
// If we are in call but we decided not to pass the key to
// the application, just pass it to the session service.
@@ -4199,10 +4208,10 @@
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
- TelephonyManager telephonyManager = getTelephonyService();
+ TelecommManager telecommManager = getTelecommService();
boolean hungUp = false;
- if (telephonyManager != null) {
- hungUp = telephonyManager.endCall();
+ if (telecommManager != null) {
+ hungUp = telecommManager.endCall();
}
interceptPowerKeyDown(!interactive || hungUp);
} else {
@@ -4238,19 +4247,19 @@
interceptScreenshotChord();
}
- TelephonyManager telephonyManager = getTelephonyService();
+ TelecommManager telecommManager = getTelecommService();
boolean hungUp = false;
- if (telephonyManager != null) {
- if (telephonyManager.isRinging()) {
+ if (telecommManager != null) {
+ if (telecommManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
- telephonyManager.silenceRinger();
+ telecommManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
- && telephonyManager.isOffhook() && interactive) {
+ && telecommManager.isInAPhoneCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
- hungUp = telephonyManager.endCall();
+ hungUp = telecommManager.endCall();
}
}
interceptPowerKeyDown(!interactive || hungUp
@@ -4286,9 +4295,9 @@
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
if (down) {
- TelephonyManager telephonyManager = getTelephonyService();
- if (telephonyManager != null) {
- if (!telephonyManager.isIdle()) {
+ TelecommManager telecommManager = getTelecommService();
+ if (telecommManager != null) {
+ if (telecommManager.isInAPhoneCall()) {
// Suppress PLAY/PAUSE toggle when phone is ringing or in-call
// to avoid music playback.
break;
@@ -4321,12 +4330,12 @@
case KeyEvent.KEYCODE_CALL: {
if (down) {
- TelephonyManager telephonyManager = getTelephonyService();
- if (telephonyManager != null) {
- if (telephonyManager.isRinging()) {
+ TelecommManager telecommManager = getTelecommService();
+ if (telecommManager != null) {
+ if (telecommManager.isRinging()) {
Log.i(TAG, "interceptKeyBeforeQueueing:"
+ " CALL key-down while ringing: Answer the call!");
- telephonyManager.answerRingingCall();
+ telecommManager.acceptRingingCall();
// And *don't* pass this key thru to the current activity
// (which is presumably the InCallScreen.)
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index bfa0402..14a462e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -29,8 +29,11 @@
import java.util.List;
import java.util.Map;
+import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioService;
@@ -102,10 +105,12 @@
public final static class Ops extends SparseArray<Op> {
public final String packageName;
public final int uid;
+ public final boolean isPrivileged;
- public Ops(String _packageName, int _uid) {
+ public Ops(String _packageName, int _uid, boolean _isPrivileged) {
packageName = _packageName;
uid = _uid;
+ isPrivileged = _isPrivileged;
}
}
@@ -368,7 +373,11 @@
@Override
public void setMode(int code, int uid, String packageName, int mode) {
- verifyIncomingUid(uid);
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
verifyIncomingOp(code);
ArrayList<Callback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
@@ -556,7 +565,7 @@
verifyIncomingUid(uid);
verifyIncomingOp(code);
synchronized (this) {
- if (isOpRestricted(uid, code)) {
+ if (isOpRestricted(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
@@ -642,7 +651,7 @@
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
- if (isOpRestricted(uid, code)) {
+ if (isOpRestricted(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
if (op.duration == -1) {
@@ -679,7 +688,7 @@
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
- if (isOpRestricted(uid, code)) {
+ if (isOpRestricted(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -778,6 +787,7 @@
if (!edit) {
return null;
}
+ boolean isPrivileged = false;
// This is the first time we have seen this package name under this uid,
// so let's make sure it is valid.
if (uid != 0) {
@@ -785,12 +795,19 @@
try {
int pkgUid = -1;
try {
- pkgUid = mContext.getPackageManager().getPackageUid(packageName,
- UserHandle.getUserId(uid));
- } catch (NameNotFoundException e) {
- if ("media".equals(packageName)) {
- pkgUid = Process.MEDIA_UID;
+ ApplicationInfo appInfo = ActivityThread.getPackageManager()
+ .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid));
+ if (appInfo != null) {
+ pkgUid = appInfo.uid;
+ isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+ } else {
+ if ("media".equals(packageName)) {
+ pkgUid = Process.MEDIA_UID;
+ isPrivileged = false;
+ }
}
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not contact PackageManager", e);
}
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
@@ -803,7 +820,7 @@
Binder.restoreCallingIdentity(ident);
}
}
- ops = new Ops(packageName, uid);
+ ops = new Ops(packageName, uid, isPrivileged);
pkgOps.put(packageName, ops);
}
return ops;
@@ -847,10 +864,18 @@
return op;
}
- private boolean isOpRestricted(int uid, int code) {
+ private boolean isOpRestricted(int uid, int code, String packageName) {
int userHandle = UserHandle.getUserId(uid);
boolean[] opRestrictions = mOpRestrictions.get(userHandle);
if ((opRestrictions != null) && opRestrictions[code]) {
+ if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+ synchronized (this) {
+ Ops ops = getOpsLocked(uid, packageName, true);
+ if ((ops != null) && ops.isPrivileged) {
+ return false;
+ }
+ }
+ }
if (userHandle == UserHandle.USER_OWNER) {
if (uid != mDeviceOwnerUid) {
return true;
@@ -955,6 +980,27 @@
void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException,
XmlPullParserException, IOException {
int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+ String isPrivilegedString = parser.getAttributeValue(null, "p");
+ boolean isPrivileged = false;
+ if (isPrivilegedString == null) {
+ try {
+ IPackageManager packageManager = ActivityThread.getPackageManager();
+ if (packageManager != null) {
+ ApplicationInfo appInfo = ActivityThread.getPackageManager()
+ .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
+ if (appInfo != null) {
+ isPrivileged = (appInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0;
+ }
+ } else {
+ // Could not load data, don't add to cache so it will be loaded later.
+ return;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not contact PackageManager", e);
+ }
+ } else {
+ isPrivileged = Boolean.parseBoolean(isPrivilegedString);
+ }
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -989,7 +1035,7 @@
}
Ops ops = pkgOps.get(pkgName);
if (ops == null) {
- ops = new Ops(pkgName, uid);
+ ops = new Ops(pkgName, uid, isPrivileged);
pkgOps.put(pkgName, ops);
}
ops.put(op.op, op);
@@ -1033,6 +1079,16 @@
}
out.startTag(null, "uid");
out.attribute(null, "n", Integer.toString(pkg.getUid()));
+ synchronized (this) {
+ Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
+ // Should always be present as the list of PackageOps is generated
+ // from Ops.
+ if (ops != null) {
+ out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
+ } else {
+ out.attribute(null, "p", Boolean.toString(false));
+ }
+ }
List<AppOpsManager.OpEntry> ops = pkg.getOps();
for (int j=0; j<ops.size(); j++) {
AppOpsManager.OpEntry op = ops.get(j);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7fe1e82..64db8cf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -331,7 +331,9 @@
/**
* used internally to clear a wakelock when transitioning
- * from one net to another
+ * from one net to another. Clear happens when we get a new
+ * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
+ * after a timeout if no network is found (typically 1 min).
*/
private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
@@ -420,6 +422,15 @@
*/
private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
+ /**
+ * used internally to expire a wakelock when transitioning
+ * from one net to another. Expire happens when we fail to find
+ * a new network (typically after 1 minute) -
+ * EVENT_CLEAR_NET_TRANSITION_WAKELOCK happens if we had found
+ * a replacement network.
+ */
+ private static final int EVENT_EXPIRE_NET_TRANSITION_WAKELOCK = 24;
+
/** Handler used for internal events. */
final private InternalHandler mHandler;
@@ -3290,6 +3301,7 @@
if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
removeDataActivityTracking(nai);
mActiveDefaultNetwork = ConnectivityManager.TYPE_NONE;
+ requestNetworkTransitionWakelock(nai.name());
}
for (NetworkAgentInfo networkToActivate : toActivate) {
networkToActivate.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
@@ -3391,6 +3403,7 @@
public void handleMessage(Message msg) {
NetworkInfo info;
switch (msg.what) {
+ case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
String causedBy = null;
synchronized (ConnectivityService.this) {
@@ -3398,10 +3411,15 @@
mNetTransitionWakeLock.isHeld()) {
mNetTransitionWakeLock.release();
causedBy = mNetTransitionWakeLockCausedBy;
+ } else {
+ break;
}
}
- if (causedBy != null) {
- log("NetTransition Wakelock for " + causedBy + " released by timeout");
+ if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
+ log("Failed to find a new network - expiring NetTransition Wakelock");
+ } else {
+ log("NetTransition Wakelock (" + (causedBy == null ? "unknown" : causedBy) +
+ " cleared because we found a replacement network");
}
break;
}
@@ -3602,15 +3620,15 @@
// restarted by subsequent callers.
public void requestNetworkTransitionWakelock(String forWhom) {
enforceConnectivityInternalPermission();
+ int serialNum = 0;
synchronized (this) {
if (mNetTransitionWakeLock.isHeld()) return;
- mNetTransitionWakeLockSerialNumber++;
+ serialNum = ++mNetTransitionWakeLockSerialNumber;
mNetTransitionWakeLock.acquire();
mNetTransitionWakeLockCausedBy = forWhom;
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(
- EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
- mNetTransitionWakeLockSerialNumber, 0),
+ EVENT_EXPIRE_NET_TRANSITION_WAKELOCK, serialNum, 0),
mNetTransitionWakeLockTimeout);
return;
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 1b71518..538501b 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -18,10 +18,12 @@
import android.Manifest.permission;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.INetworkScoreCache;
import android.net.INetworkScoreService;
+import android.net.NetworkScoreManager;
import android.net.NetworkScorerAppManager;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.net.ScoredNetwork;
@@ -133,7 +135,13 @@
// as scores should never be compared across apps; in practice, Settings should only be
// allowing valid apps to be set as scorers, so failure here should be rare.
clearInternal();
- return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ if (result) {
+ Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
+ intent.putExtra(NetworkScoreManager.EXTRA_NEW_SCORER, packageName);
+ mContext.sendBroadcast(intent);
+ }
+ return result;
}
/** Clear scores. Callers are responsible for checking permissions as appropriate. */
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
new file mode 100644
index 0000000..fdcb3b9
--- /dev/null
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -0,0 +1,349 @@
+/*
+ * 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;
+
+import android.content.pm.FeatureInfo;
+import android.os.*;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import static com.android.internal.util.ArrayUtils.appendInt;
+
+/**
+ * Loads global system configuration info.
+ */
+public class SystemConfig {
+ static final String TAG = "SystemConfig";
+
+ static SystemConfig sInstance;
+
+ // Group-ids that are given to all packages as read from etc/permissions/*.xml.
+ int[] mGlobalGids;
+
+ // These are the built-in uid -> permission mappings that were read from the
+ // system configuration files.
+ final SparseArray<HashSet<String>> mSystemPermissions =
+ new SparseArray<HashSet<String>>();
+
+ // These are the built-in shared libraries that were read from the
+ // system configuration files. Keys are the library names; strings are the
+ // paths to the libraries.
+ final ArrayMap<String, String> mSharedLibraries
+ = new ArrayMap<String, String>();
+
+ // These are the features this devices supports that were read from the
+ // system configuration files.
+ final HashMap<String, FeatureInfo> mAvailableFeatures =
+ new HashMap<String, FeatureInfo>();
+
+ public static final class PermissionEntry {
+ public final String name;
+ public int[] gids;
+
+ PermissionEntry(String _name) {
+ name = _name;
+ }
+ }
+
+ // These are the permission -> gid mappings that were read from the
+ // system configuration files.
+ final ArrayMap<String, PermissionEntry> mPermissions =
+ new ArrayMap<String, PermissionEntry>();
+
+ // These are the packages that are white-listed to be able to run in the
+ // background while in power save mode, as read from the configuration files.
+ final ArraySet<String> mAllowInPowerSave = new ArraySet<String>();
+
+ public static SystemConfig getInstance() {
+ synchronized (SystemConfig.class) {
+ if (sInstance == null) {
+ sInstance = new SystemConfig();
+ }
+ return sInstance;
+ }
+ }
+
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ public SparseArray<HashSet<String>> getSystemPermissions() {
+ return mSystemPermissions;
+ }
+
+ public ArrayMap<String, String> getSharedLibraries() {
+ return mSharedLibraries;
+ }
+
+ public HashMap<String, FeatureInfo> getAvailableFeatures() {
+ return mAvailableFeatures;
+ }
+
+ public ArrayMap<String, PermissionEntry> getPermissions() {
+ return mPermissions;
+ }
+
+ public ArraySet<String> getAllowInPowerSave() {
+ return mAllowInPowerSave;
+ }
+
+ SystemConfig() {
+ // Read configuration from system
+ readPermissions(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "sysconfig"), false);
+ // Read configuration from the old permissions dir
+ readPermissions(Environment.buildPath(
+ Environment.getRootDirectory(), "etc", "permissions"), false);
+ // Only read features from OEM config
+ readPermissions(Environment.buildPath(
+ Environment.getOemDirectory(), "etc", "sysconfig"), true);
+ readPermissions(Environment.buildPath(
+ Environment.getOemDirectory(), "etc", "permissions"), true);
+ }
+
+ void readPermissions(File libraryDir, boolean onlyFeatures) {
+ // Read permissions from given directory.
+ if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+ if (!onlyFeatures) {
+ Slog.w(TAG, "No directory " + libraryDir + ", skipping");
+ }
+ return;
+ }
+ if (!libraryDir.canRead()) {
+ Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
+ return;
+ }
+
+ // Iterate over the files in the directory and scan .xml files
+ for (File f : libraryDir.listFiles()) {
+ // We'll read platform.xml last
+ if (f.getPath().endsWith("etc/permissions/platform.xml")) {
+ continue;
+ }
+
+ if (!f.getPath().endsWith(".xml")) {
+ Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
+ continue;
+ }
+ if (!f.canRead()) {
+ Slog.w(TAG, "Permissions library file " + f + " cannot be read");
+ continue;
+ }
+
+ readPermissionsFromXml(f, onlyFeatures);
+ }
+
+ // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
+ final File permFile = new File(Environment.getRootDirectory(),
+ "etc/permissions/platform.xml");
+ readPermissionsFromXml(permFile, onlyFeatures);
+ }
+
+ private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
+ FileReader permReader = null;
+ try {
+ permReader = new FileReader(permFile);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
+ return;
+ }
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(permReader);
+
+ int type;
+ while ((type=parser.next()) != parser.START_TAG
+ && type != parser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != parser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
+ throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+ ", expected 'permissions' or 'config'");
+ }
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+
+ String name = parser.getName();
+ if ("group".equals(name) && !onlyFeatures) {
+ String gidStr = parser.getAttributeValue(null, "gid");
+ if (gidStr != null) {
+ int gid = android.os.Process.getGidForName(gidStr);
+ mGlobalGids = appendInt(mGlobalGids, gid);
+ } else {
+ Slog.w(TAG, "<group> without gid at "
+ + parser.getPositionDescription());
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ } else if ("permission".equals(name) && !onlyFeatures) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG, "<permission> without name at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ perm = perm.intern();
+ readPermission(parser, perm);
+
+ } else if ("assign-permission".equals(name) && !onlyFeatures) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG, "<assign-permission> without name at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String uidStr = parser.getAttributeValue(null, "uid");
+ if (uidStr == null) {
+ Slog.w(TAG, "<assign-permission> without uid at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ int uid = Process.getUidForName(uidStr);
+ if (uid < 0) {
+ Slog.w(TAG, "<assign-permission> with unknown uid \""
+ + uidStr + "\" at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ perm = perm.intern();
+ HashSet<String> perms = mSystemPermissions.get(uid);
+ if (perms == null) {
+ perms = new HashSet<String>();
+ mSystemPermissions.put(uid, perms);
+ }
+ perms.add(perm);
+ XmlUtils.skipCurrentTag(parser);
+
+ } else if ("library".equals(name) && !onlyFeatures) {
+ String lname = parser.getAttributeValue(null, "name");
+ String lfile = parser.getAttributeValue(null, "file");
+ if (lname == null) {
+ Slog.w(TAG, "<library> without name at "
+ + parser.getPositionDescription());
+ } else if (lfile == null) {
+ Slog.w(TAG, "<library> without file at "
+ + parser.getPositionDescription());
+ } else {
+ //Log.i(TAG, "Got library " + lname + " in " + lfile);
+ mSharedLibraries.put(lname, lfile);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else if ("feature".equals(name)) {
+ String fname = parser.getAttributeValue(null, "name");
+ if (fname == null) {
+ Slog.w(TAG, "<feature> without name at "
+ + parser.getPositionDescription());
+ } else {
+ //Log.i(TAG, "Got feature " + fname);
+ FeatureInfo fi = new FeatureInfo();
+ fi.name = fname;
+ mAvailableFeatures.put(fname, fi);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else if ("allow-in-power-save".equals(name)) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<allow-in-power-save> without package at "
+ + parser.getPositionDescription());
+ } else {
+ mAllowInPowerSave.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ }
+ permReader.close();
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Got execption parsing permissions.", e);
+ } catch (IOException e) {
+ Slog.w(TAG, "Got execption parsing permissions.", e);
+ }
+ }
+
+ void readPermission(XmlPullParser parser, String name)
+ throws IOException, XmlPullParserException {
+
+ name = name.intern();
+
+ PermissionEntry perm = mPermissions.get(name);
+ if (perm == null) {
+ perm = new PermissionEntry(name);
+ mPermissions.put(name, perm);
+ }
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if ("group".equals(tagName)) {
+ String gidStr = parser.getAttributeValue(null, "gid");
+ if (gidStr != null) {
+ int gid = Process.getGidForName(gidStr);
+ perm.gids = appendInt(perm.gids, gid);
+ } else {
+ Slog.w(TAG, "<group> without gid at "
+ + parser.getPositionDescription());
+ }
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 933046b..45367a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -36,9 +36,6 @@
import android.app.IAppTask;
import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetManager;
-import android.content.DialogInterface.OnClickListener;
-import android.content.res.Resources;
-import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.os.BatteryStats;
import android.os.PersistableBundle;
@@ -174,12 +171,8 @@
import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
-import android.text.Spannable;
-import android.text.SpannableString;
import android.text.format.DateUtils;
import android.text.format.Time;
-import android.text.style.DynamicDrawableSpan;
-import android.text.style.ImageSpan;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
@@ -193,7 +186,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import android.widget.TextView;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -15028,7 +15020,6 @@
int schedGroup;
int procState;
boolean foregroundActivities = false;
- boolean interesting = false;
BroadcastQueue queue;
if (app == TOP_APP) {
// The last app on the list is the foreground app.
@@ -15036,14 +15027,12 @@
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "top-activity";
foregroundActivities = true;
- interesting = true;
procState = ActivityManager.PROCESS_STATE_TOP;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "instrumentation";
- interesting = true;
procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
} else if ((queue = isReceivingBroadcast(app)) != null) {
// An app that is currently receiving a broadcast also
@@ -15159,10 +15148,6 @@
}
}
- if (app.foregroundServices) {
- interesting = true;
- }
-
if (app == mHeavyWeightProcess) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e1c7da9..1ecb43c 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1936,6 +1936,9 @@
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
+ } else if (options != null && new ActivityOptions(options).getAnimationType()
+ == ActivityOptions.ANIM_SCENE_TRANSITION) {
+ doShow = false;
}
if (SHOW_APP_STARTING_PREVIEW && doShow) {
// Figure out if we are transitioning from another activity that is
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3cbc6e2..5c5e3e3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -365,6 +365,20 @@
}
}
+ public void noteFlashlightOn() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFlashlightOnLocked();
+ }
+ }
+
+ public void noteFlashlightOff() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFlashlightOffLocked();
+ }
+ }
+
public void noteWifiRunning(WorkSource ws) {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0bd7ef9..3ef9494 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -131,8 +131,8 @@
taskId = _taskId;
voiceSession = _voiceSession;
voiceInteractor = _voiceInteractor;
- setIntent(_intent, info);
mActivities = new ArrayList<ActivityRecord>();
+ setIntent(_intent, info);
}
TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 4fbe845..49f2d6f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -318,9 +318,9 @@
*
* @param flag a key of option. For more details, look at
* {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
- * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}
- * @param value a value of option. Actual value varies flag. For more
- * details, look at description of flags
+ * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}.
+ * @param value a value of option. Actual value varies from flag to flag. For more
+ * details, look at description of flags.
*/
@ServiceThreadOnly
void setOption(int flag, int value) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 4a0eb50..72d44fa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -78,6 +78,9 @@
// This is not thread-safe. For external purpose use mSafeDeviceInfos.
private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
+ // If true, TV going to standby mode puts other devices also to standby.
+ private boolean mAutoDeviceOff;
+
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
mPrevPortId = HdmiConstants.INVALID_PORT_ID;
@@ -496,6 +499,7 @@
new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback));
}
+ // # Seq 25
void setSystemAudioMode(boolean on) {
synchronized (mLock) {
if (on != mSystemAudioMode) {
@@ -975,4 +979,10 @@
hotplugActions.get(0).pollAllDevicesNow();
}
}
+
+ @ServiceThreadOnly
+ void setAutoDeviceOff(boolean enabled) {
+ assertRunOnServiceThread();
+ mAutoDeviceOff = enabled;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 08413ec..c03bc99 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -724,7 +724,7 @@
}
@Override
- public void setControlEnabled(boolean enabled) {
+ public void setControlEnabled(final boolean enabled) {
enforceAccessPermission();
synchronized (mLock) {
mHdmiControlEnabled = enabled;
@@ -741,6 +741,11 @@
if (tv == null) {
return;
}
+ int value = enabled ? HdmiCec.ENABLED : HdmiCec.DISABLED;
+ mCecController.setOption(HdmiCec.OPTION_CEC_ENABLE, value);
+ if (mMhlController != null) {
+ mMhlController.setOption(HdmiCec.OPTION_MHL_ENABLE, value);
+ }
tv.routingAtEnableTime();
}
});
@@ -760,6 +765,32 @@
}
});
}
+
+ @Override
+ public void setOption(final int key, final int value) {
+ if (!isTvDevice()) {
+ return;
+ }
+ switch (key) {
+ case HdmiCec.OPTION_CEC_AUTO_WAKEUP:
+ mCecController.setOption(key, value);
+ break;
+ case HdmiCec.OPTION_CEC_AUTO_DEVICE_OFF:
+ // No need to pass this option to HAL.
+ tv().setAutoDeviceOff(value == HdmiCec.ENABLED);
+ break;
+ case HdmiCec.OPTION_MHL_INPUT_SWITCHING: // Fall through
+ case HdmiCec.OPTION_MHL_POWER_CHARGE:
+ if (mMhlController != null) {
+ mMhlController.setOption(key, value);
+ }
+ break;
+ }
+ }
+
+ private boolean isTvDevice() {
+ return tv() != null;
+ }
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index 9d34589..f44c014 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -22,6 +22,7 @@
/**
* Feature action that handles System Audio initiated by AVR devices.
*/
+// # Seq 33
final class SystemAudioActionFromAvr extends SystemAudioAction {
/**
* Constructor
@@ -58,7 +59,9 @@
sendSystemAudioModeRequest();
return;
}
- // TODO: Stop the action for System Audio Mode initialization if it is running.
+
+ removeAction(SystemAudioAutoInitiationAction.class);
+
if (mTargetAudioStatus) {
setSystemAudioMode(true);
startAudioStatusAction();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 416a6b1..f0ec3bb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -39,6 +39,7 @@
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -74,6 +75,7 @@
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
@@ -111,6 +113,7 @@
import android.os.IPowerManager;
import android.os.Message;
import android.os.MessageQueue.IdleHandler;
+import android.os.PowerManagerInternal;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -119,6 +122,8 @@
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.NtpTrustedTime;
@@ -133,9 +138,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -150,10 +155,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import libcore.io.IoUtils;
@@ -224,8 +226,6 @@
private static final int MSG_RULES_CHANGED = 1;
private static final int MSG_METERED_IFACES_CHANGED = 2;
- private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 3;
- private static final int MSG_PROCESS_DIED = 4;
private static final int MSG_LIMIT_REACHED = 5;
private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
@@ -240,41 +240,51 @@
private IConnectivityManager mConnManager;
private INotificationManager mNotifManager;
+ private PowerManagerInternal mPowerManagerInternal;
- private final Object mRulesLock = new Object();
+ final Object mRulesLock = new Object();
- private volatile boolean mScreenOn;
- private volatile boolean mRestrictBackground;
+ volatile boolean mScreenOn;
+ volatile boolean mRestrictBackground;
+ volatile boolean mRestrictPower;
private final boolean mSuppressDefaultPolicy;
/** Defined network policies. */
- private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap();
+ final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<
+ NetworkTemplate, NetworkPolicy>();
/** Currently active network rules for ifaces. */
- private HashMap<NetworkPolicy, String[]> mNetworkRules = Maps.newHashMap();
+ private final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<
+ NetworkPolicy, String[]>();
/** Defined UID policies. */
- private SparseIntArray mUidPolicy = new SparseIntArray();
+ final SparseIntArray mUidPolicy = new SparseIntArray();
/** Currently derived rules for each UID. */
- private SparseIntArray mUidRules = new SparseIntArray();
+ private final SparseIntArray mUidRules = new SparseIntArray();
+
+ /** UIDs that have been white-listed to always be able to have network access in
+ * power save mode. */
+ private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray();
/** Set of ifaces that are metered. */
- private HashSet<String> mMeteredIfaces = Sets.newHashSet();
+ private ArraySet<String> mMeteredIfaces = new ArraySet<String>();
/** Set of over-limit templates that have been notified. */
- private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
+ private final ArraySet<NetworkTemplate> mOverLimitNotified = new ArraySet<NetworkTemplate>();
/** Set of currently active {@link Notification} tags. */
- private HashSet<String> mActiveNotifs = Sets.newHashSet();
+ private final ArraySet<String> mActiveNotifs = new ArraySet<String>();
/** Foreground at both UID and PID granularity. */
- private SparseBooleanArray mUidForeground = new SparseBooleanArray();
- private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
- SparseBooleanArray>();
+ private final SparseIntArray mUidState = new SparseIntArray();
+ final SparseArray<SparseIntArray> mUidPidState = new SparseArray<SparseIntArray>();
+
+ /** The current maximum process state that we are considering to be foreground. */
+ private int mCurForegroundState = ActivityManager.PROCESS_STATE_TOP;
private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
INetworkPolicyListener>();
- private final Handler mHandler;
+ final Handler mHandler;
private final AtomicFile mPolicyFile;
@@ -328,12 +338,42 @@
return;
}
+ final PackageManager pm = mContext.getPackageManager();
+
synchronized (mRulesLock) {
+ SystemConfig sysConfig = SystemConfig.getInstance();
+ ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
+ for (int i=0; i<allowPower.size(); i++) {
+ String pkg = allowPower.valueAt(i);
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+ if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ mPowerSaveWhitelistAppIds.put(UserHandle.getAppId(ai.uid), true);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean enabled) {
+ synchronized (mRulesLock) {
+ if (mRestrictPower != enabled) {
+ mRestrictPower = enabled;
+ updateRulesForGlobalChangeLocked(true);
+ }
+ }
+ }
+ });
+ mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled();
+
// read policy from disk
readPolicyLocked();
- if (mRestrictBackground) {
- updateRulesForRestrictBackgroundLocked();
+ if (mRestrictBackground || mRestrictPower) {
+ updateRulesForGlobalChangeLocked(true);
updateNotificationsLocked();
}
}
@@ -405,17 +445,38 @@
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@Override
public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
- mHandler.obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED,
- pid, uid, foregroundActivities).sendToTarget();
}
@Override
public void onProcessStateChanged(int pid, int uid, int procState) {
+ synchronized (mRulesLock) {
+ // because a uid can have multiple pids running inside, we need to
+ // remember all pid states and summarize foreground at uid level.
+
+ // record foreground for this specific pid
+ SparseIntArray pidState = mUidPidState.get(uid);
+ if (pidState == null) {
+ pidState = new SparseIntArray(2);
+ mUidPidState.put(uid, pidState);
+ }
+ pidState.put(pid, procState);
+ computeUidStateLocked(uid);
+ }
}
@Override
public void onProcessDied(int pid, int uid) {
- mHandler.obtainMessage(MSG_PROCESS_DIED, pid, uid).sendToTarget();
+ synchronized (mRulesLock) {
+ // clear records and recompute, when they exist
+ final SparseIntArray pidState = mUidPidState.get(uid);
+ if (pidState != null) {
+ pidState.delete(pid);
+ if (pidState.size() <= 0) {
+ mUidPidState.remove(uid);
+ }
+ computeUidStateLocked(uid);
+ }
+ }
}
};
@@ -476,13 +537,12 @@
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userId == -1) return;
- // Remove any policies for given user; both cleaning up after a
- // USER_REMOVED, and one last sanity check during USER_ADDED
- removePoliciesForUserLocked(userId);
-
- // Update global restrict for new user
synchronized (mRulesLock) {
- updateRulesForRestrictBackgroundLocked();
+ // Remove any policies for given user; both cleaning up after a
+ // USER_REMOVED, and one last sanity check during USER_ADDED
+ removePoliciesForUserLocked(userId);
+ // Update global restrict for new user
+ updateRulesForGlobalChangeLocked(true);
}
}
};
@@ -620,12 +680,11 @@
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
*/
- private void updateNotificationsLocked() {
+ void updateNotificationsLocked() {
if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
// keep track of previously active notifications
- final HashSet<String> beforeNotifs = Sets.newHashSet();
- beforeNotifs.addAll(mActiveNotifs);
+ final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
mActiveNotifs.clear();
// TODO: when switching to kernel notifications, compute next future
@@ -633,7 +692,8 @@
// examine stats for each active policy
final long currentTime = currentTimeMillis();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// ignore policies that aren't relevant to user
if (!isTemplateRelevant(policy.template)) continue;
if (!policy.hasCycle()) continue;
@@ -665,7 +725,8 @@
}
// cancel stale notifications that we didn't renew above
- for (String tag : beforeNotifs) {
+ for (int i = beforeNotifs.size()-1; i >= 0; i--) {
+ final String tag = beforeNotifs.valueAt(i);
if (!mActiveNotifs.contains(tag)) {
cancelNotification(tag);
}
@@ -905,14 +966,15 @@
* Proactively control network data connections when they exceed
* {@link NetworkPolicy#limitBytes}.
*/
- private void updateNetworkEnabledLocked() {
+ void updateNetworkEnabledLocked() {
if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
// TODO: reset any policy-disabled networks when any policy is removed
// completely, which is currently rare case.
final long currentTime = currentTimeMillis();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// shortcut when policy has no limit
if (policy.limitBytes == LIMIT_DISABLED || !policy.hasCycle()) {
setNetworkTemplateEnabled(policy.template, true);
@@ -967,7 +1029,7 @@
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
*/
- private void updateNetworkRulesLocked() {
+ void updateNetworkRulesLocked() {
if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
final NetworkState[] states;
@@ -978,29 +1040,42 @@
return;
}
+ // If we are in restrict power mode, we want to treat all interfaces
+ // as metered, to restrict access to the network by uid. However, we
+ // will not have a bandwidth limit. Also only do this if restrict
+ // background data use is *not* enabled, since that takes precendence
+ // use over those networks can have a cost associated with it).
+ final boolean powerSave = mRestrictPower && !mRestrictBackground;
+
// first, derive identity for all connected networks, which can be used
// to match against templates.
- final HashMap<NetworkIdentity, String> networks = Maps.newHashMap();
+ final ArrayMap<NetworkIdentity, String> networks = new ArrayMap<NetworkIdentity,
+ String>(states.length);
+ final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
for (NetworkState state : states) {
// stash identity and iface away for later use
if (state.networkInfo.isConnected()) {
final String iface = state.linkProperties.getInterfaceName();
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
networks.put(ident, iface);
+ if (powerSave) {
+ connIfaces.add(iface);
+ }
}
}
// build list of rules and ifaces to enforce them against
mNetworkRules.clear();
final ArrayList<String> ifaceList = Lists.newArrayList();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// collect all active ifaces that match this template
ifaceList.clear();
- for (Map.Entry<NetworkIdentity, String> entry : networks.entrySet()) {
- final NetworkIdentity ident = entry.getKey();
+ for (int j = networks.size()-1; j >= 0; j--) {
+ final NetworkIdentity ident = networks.keyAt(j);
if (policy.template.matches(ident)) {
- final String iface = entry.getValue();
+ final String iface = networks.valueAt(j);
ifaceList.add(iface);
}
}
@@ -1012,13 +1087,14 @@
}
long lowestRule = Long.MAX_VALUE;
- final HashSet<String> newMeteredIfaces = Sets.newHashSet();
+ final ArraySet<String> newMeteredIfaces = new ArraySet<String>(states.length);
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
final long currentTime = currentTimeMillis();
- for (NetworkPolicy policy : mNetworkRules.keySet()) {
- final String[] ifaces = mNetworkRules.get(policy);
+ for (int i = mNetworkRules.size()-1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkRules.keyAt(i);
+ final String[] ifaces = mNetworkRules.valueAt(i);
final long start;
final long totalBytes;
@@ -1063,6 +1139,9 @@
removeInterfaceQuota(iface);
setInterfaceQuota(iface, quotaBytes);
newMeteredIfaces.add(iface);
+ if (powerSave) {
+ connIfaces.remove(iface);
+ }
}
}
@@ -1075,10 +1154,18 @@
}
}
+ for (int i = connIfaces.size()-1; i >= 0; i--) {
+ String iface = connIfaces.valueAt(i);
+ removeInterfaceQuota(iface);
+ setInterfaceQuota(iface, Long.MAX_VALUE);
+ newMeteredIfaces.add(iface);
+ }
+
mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
// remove quota on any trailing interfaces
- for (String iface : mMeteredIfaces) {
+ for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
+ final String iface = mMeteredIfaces.valueAt(i);
if (!newMeteredIfaces.contains(iface)) {
removeInterfaceQuota(iface);
}
@@ -1108,9 +1195,10 @@
// examine to see if any policy is defined for active mobile
boolean mobileDefined = false;
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- if (policy.template.matches(probeIdent)) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ if (mNetworkPolicy.valueAt(i).template.matches(probeIdent)) {
mobileDefined = true;
+ break;
}
}
@@ -1226,7 +1314,7 @@
final int policy = readIntAttribute(in, ATTR_POLICY);
if (UserHandle.isApp(uid)) {
- setUidPolicyUnchecked(uid, policy, false);
+ setUidPolicyUncheckedLocked(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
@@ -1237,7 +1325,7 @@
// TODO: set for other users during upgrade
final int uid = UserHandle.getUid(UserHandle.USER_OWNER, appId);
if (UserHandle.isApp(uid)) {
- setUidPolicyUnchecked(uid, policy, false);
+ setUidPolicyUncheckedLocked(uid, policy, false);
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
@@ -1273,7 +1361,7 @@
}
}
- private void writePolicyLocked() {
+ void writePolicyLocked() {
if (LOGV) Slog.v(TAG, "writePolicyLocked()");
FileOutputStream fos = null;
@@ -1289,7 +1377,8 @@
writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
// write all known network policies
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = 0; i < mNetworkPolicy.size(); i++) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
final NetworkTemplate template = policy.template;
out.startTag(null, TAG_NETWORK_POLICY);
@@ -1346,24 +1435,59 @@
throw new IllegalArgumentException("cannot apply policy to UID " + uid);
}
- setUidPolicyUnchecked(uid, policy, true);
+ synchronized (mRulesLock) {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
+ }
+ }
}
- private void setUidPolicyUnchecked(int uid, int policy, boolean persist) {
- final int oldPolicy;
- synchronized (mRulesLock) {
- oldPolicy = getUidPolicy(uid);
- mUidPolicy.put(uid, policy);
+ @Override
+ public void addUidPolicy(int uid, int policy) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- // uid policy changed, recompute rules and persist policy.
- updateRulesForUidLocked(uid);
- if (persist) {
- writePolicyLocked();
+ if (!UserHandle.isApp(uid)) {
+ throw new IllegalArgumentException("cannot apply policy to UID " + uid);
+ }
+
+ synchronized (mRulesLock) {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ policy |= oldPolicy;
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
}
}
}
@Override
+ public void removeUidPolicy(int uid, int policy) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ if (!UserHandle.isApp(uid)) {
+ throw new IllegalArgumentException("cannot apply policy to UID " + uid);
+ }
+
+ synchronized (mRulesLock) {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ policy = oldPolicy & ~policy;
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
+ }
+ }
+ }
+
+ private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {
+ mUidPolicy.put(uid, policy);
+
+ // uid policy changed, recompute rules and persist policy.
+ updateRulesForUidLocked(uid);
+ if (persist) {
+ writePolicyLocked();
+ }
+ }
+
+ @Override
public int getUidPolicy(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
@@ -1389,11 +1513,25 @@
return uids;
}
+ @Override
+ public int[] getPowerSaveAppIdWhitelist() {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mRulesLock) {
+ int size = mPowerSaveWhitelistAppIds.size();
+ int[] appids = new int[size];
+ for (int i = 0; i < size; i++) {
+ appids[i] = mPowerSaveWhitelistAppIds.keyAt(i);
+ }
+ return appids;
+ }
+ }
+
/**
* Remove any policies associated with given {@link UserHandle}, persisting
* if any changes are made.
*/
- private void removePoliciesForUserLocked(int userId) {
+ void removePoliciesForUserLocked(int userId) {
if (LOGV) Slog.v(TAG, "removePoliciesForUserLocked()");
int[] uids = new int[0];
@@ -1449,7 +1587,7 @@
}
}
- private void addNetworkPolicyLocked(NetworkPolicy policy) {
+ void addNetworkPolicyLocked(NetworkPolicy policy) {
mNetworkPolicy.put(policy.template, policy);
updateNetworkEnabledLocked();
@@ -1480,7 +1618,7 @@
}
}
- private void performSnooze(NetworkTemplate template, int type) {
+ void performSnooze(NetworkTemplate template, int type) {
maybeRefreshTrustedTime();
final long currentTime = currentTimeMillis();
synchronized (mRulesLock) {
@@ -1515,7 +1653,7 @@
maybeRefreshTrustedTime();
synchronized (mRulesLock) {
mRestrictBackground = restrictBackground;
- updateRulesForRestrictBackgroundLocked();
+ updateRulesForGlobalChangeLocked(false);
updateNotificationsLocked();
writePolicyLocked();
}
@@ -1534,7 +1672,8 @@
}
private NetworkPolicy findPolicyForNetworkLocked(NetworkIdentity ident) {
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ NetworkPolicy policy = mNetworkPolicy.valueAt(i);
if (policy.template.matches(ident)) {
return policy;
}
@@ -1616,15 +1755,15 @@
final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " ");
- final HashSet<String> argSet = new HashSet<String>();
+ final ArraySet<String> argSet = new ArraySet<String>(args.length);
for (String arg : args) {
argSet.add(arg);
}
synchronized (mRulesLock) {
if (argSet.contains("--unsnooze")) {
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- policy.clearSnooze();
+ for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
+ mNetworkPolicy.valueAt(i).clearSnooze();
}
updateNetworkEnabledLocked();
@@ -1637,10 +1776,12 @@
}
fout.print("Restrict background: "); fout.println(mRestrictBackground);
+ fout.print("Restrict power: "); fout.println(mRestrictPower);
+ fout.print("Current foreground state: "); fout.println(mCurForegroundState);
fout.println("Network policies:");
fout.increaseIndent();
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- fout.println(policy.toString());
+ for (int i = 0; i < mNetworkPolicy.size(); i++) {
+ fout.println(mNetworkPolicy.valueAt(i).toString());
}
fout.decreaseIndent();
@@ -1658,8 +1799,22 @@
}
fout.decreaseIndent();
+ size = mPowerSaveWhitelistAppIds.size();
+ if (size > 0) {
+ fout.println("Power save whitelist app ids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mPowerSaveWhitelistAppIds.keyAt(i));
+ fout.print(": ");
+ fout.print(mPowerSaveWhitelistAppIds.valueAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
final SparseBooleanArray knownUids = new SparseBooleanArray();
- collectKeys(mUidForeground, knownUids);
+ collectKeys(mUidState, knownUids);
collectKeys(mUidRules, knownUids);
fout.println("Status for known UIDs:");
@@ -1670,12 +1825,17 @@
fout.print("UID=");
fout.print(uid);
- fout.print(" foreground=");
- final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
+ int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ fout.print(" state=");
+ fout.print(state);
+ fout.print(state <= mCurForegroundState ? " (fg)" : " (bg)");
+
+ fout.print(" pids=");
+ final int foregroundIndex = mUidPidState.indexOfKey(uid);
if (foregroundIndex < 0) {
fout.print("UNKNOWN");
} else {
- dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
+ dumpSparseIntArray(fout, mUidPidState.valueAt(foregroundIndex));
}
fout.print(" rules=");
@@ -1697,33 +1857,44 @@
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
synchronized (mRulesLock) {
- // only really in foreground when screen is also on
- return mUidForeground.get(uid, false) && mScreenOn;
+ return isUidForegroundLocked(uid);
}
}
+ boolean isUidForegroundLocked(int uid) {
+ // only really in foreground when screen is also on
+ return mScreenOn && mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)
+ <= mCurForegroundState;
+ }
+
/**
- * Foreground for PID changed; recompute foreground at UID level. If
+ * Process state of PID changed; recompute state at UID level. If
* changed, will trigger {@link #updateRulesForUidLocked(int)}.
*/
- private void computeUidForegroundLocked(int uid) {
- final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ void computeUidStateLocked(int uid) {
+ final SparseIntArray pidState = mUidPidState.get(uid);
// current pid is dropping foreground; examine other pids
- boolean uidForeground = false;
- final int size = pidForeground.size();
- for (int i = 0; i < size; i++) {
- if (pidForeground.valueAt(i)) {
- uidForeground = true;
- break;
+ int uidState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ if (pidState != null) {
+ final int size = pidState.size();
+ for (int i = 0; i < size; i++) {
+ final int state = pidState.valueAt(i);
+ if (state < uidState) {
+ uidState = state;
+ }
}
}
- final boolean oldUidForeground = mUidForeground.get(uid, false);
- if (oldUidForeground != uidForeground) {
- // foreground changed, push updated rules
- mUidForeground.put(uid, uidForeground);
- updateRulesForUidLocked(uid);
+ final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ if (oldUidState != uidState) {
+ // state changed, push updated rules
+ mUidState.put(uid, uidState);
+ final boolean oldForeground = oldUidState <= mCurForegroundState;
+ final boolean newForeground = uidState <= mCurForegroundState;
+ if (oldForeground != newForeground) {
+ updateRulesForUidLocked(uid);
+ }
}
}
@@ -1743,22 +1914,30 @@
*/
private void updateRulesForScreenLocked() {
// only update rules for anyone with foreground activities
- final int size = mUidForeground.size();
+ final int size = mUidState.size();
for (int i = 0; i < size; i++) {
- if (mUidForeground.valueAt(i)) {
- final int uid = mUidForeground.keyAt(i);
+ if (mUidState.valueAt(i) <= mCurForegroundState) {
+ final int uid = mUidState.keyAt(i);
updateRulesForUidLocked(uid);
}
}
}
/**
- * Update rules that might be changed by {@link #mRestrictBackground} value.
+ * Update rules that might be changed by {@link #mRestrictBackground}
+ * or {@link #mRestrictPower} value.
*/
- private void updateRulesForRestrictBackgroundLocked() {
+ void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
final PackageManager pm = mContext.getPackageManager();
final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ // If we are in restrict power mode, we allow all important apps
+ // to have data access. Otherwise, we restrict data access to only
+ // the top apps.
+ mCurForegroundState = (!mRestrictBackground && mRestrictPower)
+ ? ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ : ActivityManager.PROCESS_STATE_TOP;
+
// update rules for all installed applications
final List<UserInfo> users = um.getUsers();
final List<ApplicationInfo> apps = pm.getInstalledApplications(
@@ -1774,6 +1953,11 @@
// limit data usage for some internal system services
updateRulesForUidLocked(android.os.Process.MEDIA_UID);
updateRulesForUidLocked(android.os.Process.DRM_UID);
+
+ // If the set of restricted networks may have changed, re-evaluate those.
+ if (restrictedNetworksChanged) {
+ updateNetworkRulesLocked();
+ }
}
private static boolean isUidValidForRules(int uid) {
@@ -1786,21 +1970,31 @@
return false;
}
- private void updateRulesForUidLocked(int uid) {
+ void updateRulesForUidLocked(int uid) {
if (!isUidValidForRules(uid)) return;
- final int uidPolicy = getUidPolicy(uid);
- final boolean uidForeground = isUidForeground(uid);
+ final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ final boolean uidForeground = isUidForegroundLocked(uid);
// derive active rules based on policy and active state
int uidRules = RULE_ALLOW_ALL;
if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
- }
- if (!uidForeground && mRestrictBackground) {
- // uid in background, and global background disabled
- uidRules = RULE_REJECT_METERED;
+ } else if (mRestrictBackground) {
+ if (!uidForeground) {
+ // uid in background, and global background disabled
+ uidRules = RULE_REJECT_METERED;
+ }
+ } else if (mRestrictPower) {
+ final boolean whitelisted = mPowerSaveWhitelistAppIds.get(UserHandle.getAppId(uid));
+ if (!whitelisted && !uidForeground
+ && (uidPolicy & POLICY_ALLOW_BACKGROUND_BATTERY_SAVE) == 0) {
+ // uid is in background, restrict power use mode is on (so we want to
+ // restrict all background network access), and this uid is not on the
+ // white list of those allowed background access.
+ uidRules = RULE_REJECT_METERED;
+ }
}
// TODO: only dispatch when rules actually change
@@ -1860,40 +2054,6 @@
mListeners.finishBroadcast();
return true;
}
- case MSG_FOREGROUND_ACTIVITIES_CHANGED: {
- final int pid = msg.arg1;
- final int uid = msg.arg2;
- final boolean foregroundActivities = (Boolean) msg.obj;
-
- synchronized (mRulesLock) {
- // because a uid can have multiple pids running inside, we need to
- // remember all pid states and summarize foreground at uid level.
-
- // record foreground for this specific pid
- SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
- if (pidForeground == null) {
- pidForeground = new SparseBooleanArray(2);
- mUidPidForeground.put(uid, pidForeground);
- }
- pidForeground.put(pid, foregroundActivities);
- computeUidForegroundLocked(uid);
- }
- return true;
- }
- case MSG_PROCESS_DIED: {
- final int pid = msg.arg1;
- final int uid = msg.arg2;
-
- synchronized (mRulesLock) {
- // clear records and recompute, when they exist
- final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
- if (pidForeground != null) {
- pidForeground.delete(pid);
- computeUidForegroundLocked(uid);
- }
- }
- return true;
- }
case MSG_LIMIT_REACHED: {
final String iface = (String) msg.obj;
@@ -2020,7 +2180,7 @@
/**
* Try refreshing {@link #mTime} when stale.
*/
- private void maybeRefreshTrustedTime() {
+ void maybeRefreshTrustedTime() {
if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
mTime.forceRefresh();
}
@@ -2070,14 +2230,7 @@
}
}
- private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
- final int size = source.size();
- for (int i = 0; i < size; i++) {
- target.put(source.keyAt(i), true);
- }
- }
-
- private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
+ private static void dumpSparseIntArray(PrintWriter fout, SparseIntArray value) {
fout.print("[");
final int size = value.size();
for (int i = 0; i < size; i++) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 89ab2ae..0658eee 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -51,6 +51,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import libcore.io.IoUtils;
import libcore.io.Libcore;
import java.io.File;
@@ -449,8 +450,9 @@
}
for (File file : files) {
- final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(file);
+ NativeLibraryHelper.ApkHandle handle = null;
try {
+ handle = NativeLibraryHelper.ApkHandle.create(file);
final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle,
Build.SUPPORTED_ABIS);
if (abiIndex >= 0) {
@@ -464,8 +466,11 @@
throw new InstallFailedException(abiIndex,
"Failed to copy native libraries for " + file);
}
+ } catch (IOException ioe) {
+ throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to create handle for " + file);
} finally {
- handle.close();
+ IoUtils.closeQuietly(handle);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cac27bc..9471cff 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -36,6 +36,7 @@
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.ArrayUtils.removeInt;
+import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
@@ -50,6 +51,7 @@
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
import com.android.server.Watchdog;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -103,6 +105,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
@@ -391,17 +394,20 @@
final Settings mSettings;
boolean mRestoredSettings;
- // Group-ids that are given to all packages as read from etc/permissions/*.xml.
- int[] mGlobalGids;
+ // System configuration read by SystemConfig.
+ final int[] mGlobalGids;
+ final SparseArray<HashSet<String>> mSystemPermissions;
+ final HashMap<String, FeatureInfo> mAvailableFeatures;
- // These are the built-in uid -> permission mappings that were read from the
- // etc/permissions.xml file.
- final SparseArray<HashSet<String>> mSystemPermissions =
- new SparseArray<HashSet<String>>();
+ // If mac_permissions.xml was found for seinfo labeling.
+ boolean mFoundPolicyFile;
- static final class SharedLibraryEntry {
- final String path;
- final String apk;
+ // If a recursive restorecon of /data/data/<pkg> is needed.
+ private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+
+ public static final class SharedLibraryEntry {
+ public final String path;
+ public final String apk;
SharedLibraryEntry(String _path, String _apk) {
path = _path;
@@ -409,21 +415,9 @@
}
}
- // These are the built-in shared libraries that were read from the
- // etc/permissions.xml file.
- final HashMap<String, SharedLibraryEntry> mSharedLibraries
- = new HashMap<String, SharedLibraryEntry>();
-
- // These are the features this devices supports that were read from the
- // etc/permissions.xml file.
- final HashMap<String, FeatureInfo> mAvailableFeatures =
- new HashMap<String, FeatureInfo>();
-
- // If mac_permissions.xml was found for seinfo labeling.
- boolean mFoundPolicyFile;
-
- // If a recursive restorecon of /data/data/<pkg> is needed.
- private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+ // Currently known shared libraries.
+ final HashMap<String, SharedLibraryEntry> mSharedLibraries =
+ new HashMap<String, SharedLibraryEntry>();
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
@@ -467,6 +461,9 @@
HashSet<PackageParser.Package> mDeferredDexOpt = null;
+ // Cache of users who need badging.
+ SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
+
/** Token for keys in mPendingVerification. */
private int mPendingVerificationToken = 0;
@@ -1331,6 +1328,11 @@
getDefaultDisplayMetrics(context, mMetrics);
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ mGlobalGids = systemConfig.getGlobalGids();
+ mSystemPermissions = systemConfig.getSystemPermissions();
+ mAvailableFeatures = systemConfig.getAvailableFeatures();
+
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
@@ -1352,12 +1354,26 @@
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
- // Read permissions and features from system
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "permissions"), false);
- // Only read features from OEM
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "permissions"), true);
+ // Propagate permission configuration in to package manager.
+ ArrayMap<String, SystemConfig.PermissionEntry> permConfig
+ = systemConfig.getPermissions();
+ for (int i=0; i<permConfig.size(); i++) {
+ SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
+ BasePermission bp = mSettings.mPermissions.get(perm.name);
+ if (bp == null) {
+ bp = new BasePermission(perm.name, null, BasePermission.TYPE_BUILTIN);
+ mSettings.mPermissions.put(perm.name, bp);
+ }
+ if (perm.gids != null) {
+ bp.gids = appendInts(bp.gids, perm.gids);
+ }
+ }
+
+ ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
+ for (int i=0; i<libConfig.size(); i++) {
+ mSharedLibraries.put(libConfig.keyAt(i),
+ new SharedLibraryEntry(libConfig.valueAt(i), null));
+ }
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
@@ -1822,198 +1838,6 @@
mSettings.removePackageLPw(ps.name);
}
- void readPermissions(File libraryDir, boolean onlyFeatures) {
- // Read permissions from .../etc/permission directory.
- if (!libraryDir.exists() || !libraryDir.isDirectory()) {
- Slog.w(TAG, "No directory " + libraryDir + ", skipping");
- return;
- }
- if (!libraryDir.canRead()) {
- Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
- return;
- }
-
- // Iterate over the files in the directory and scan .xml files
- for (File f : libraryDir.listFiles()) {
- // We'll read platform.xml last
- if (f.getPath().endsWith("etc/permissions/platform.xml")) {
- continue;
- }
-
- if (!f.getPath().endsWith(".xml")) {
- Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
- continue;
- }
- if (!f.canRead()) {
- Slog.w(TAG, "Permissions library file " + f + " cannot be read");
- continue;
- }
-
- readPermissionsFromXml(f, onlyFeatures);
- }
-
- // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
- final File permFile = new File(Environment.getRootDirectory(),
- "etc/permissions/platform.xml");
- readPermissionsFromXml(permFile, onlyFeatures);
- }
-
- private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
- FileReader permReader = null;
- try {
- permReader = new FileReader(permFile);
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
- return;
- }
-
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(permReader);
-
- XmlUtils.beginDocument(parser, "permissions");
-
- while (true) {
- XmlUtils.nextElement(parser);
- if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
- break;
- }
-
- String name = parser.getName();
- if ("group".equals(name) && !onlyFeatures) {
- String gidStr = parser.getAttributeValue(null, "gid");
- if (gidStr != null) {
- int gid = Process.getGidForName(gidStr);
- mGlobalGids = appendInt(mGlobalGids, gid);
- } else {
- Slog.w(TAG, "<group> without gid at "
- + parser.getPositionDescription());
- }
-
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else if ("permission".equals(name) && !onlyFeatures) {
- String perm = parser.getAttributeValue(null, "name");
- if (perm == null) {
- Slog.w(TAG, "<permission> without name at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- perm = perm.intern();
- readPermission(parser, perm);
-
- } else if ("assign-permission".equals(name) && !onlyFeatures) {
- String perm = parser.getAttributeValue(null, "name");
- if (perm == null) {
- Slog.w(TAG, "<assign-permission> without name at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- String uidStr = parser.getAttributeValue(null, "uid");
- if (uidStr == null) {
- Slog.w(TAG, "<assign-permission> without uid at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- int uid = Process.getUidForName(uidStr);
- if (uid < 0) {
- Slog.w(TAG, "<assign-permission> with unknown uid \""
- + uidStr + "\" at "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- perm = perm.intern();
- HashSet<String> perms = mSystemPermissions.get(uid);
- if (perms == null) {
- perms = new HashSet<String>();
- mSystemPermissions.put(uid, perms);
- }
- perms.add(perm);
- XmlUtils.skipCurrentTag(parser);
-
- } else if ("library".equals(name) && !onlyFeatures) {
- String lname = parser.getAttributeValue(null, "name");
- String lfile = parser.getAttributeValue(null, "file");
- if (lname == null) {
- Slog.w(TAG, "<library> without name at "
- + parser.getPositionDescription());
- } else if (lfile == null) {
- Slog.w(TAG, "<library> without file at "
- + parser.getPositionDescription());
- } else {
- //Log.i(TAG, "Got library " + lname + " in " + lfile);
- mSharedLibraries.put(lname, new SharedLibraryEntry(lfile, null));
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else if ("feature".equals(name)) {
- String fname = parser.getAttributeValue(null, "name");
- if (fname == null) {
- Slog.w(TAG, "<feature> without name at "
- + parser.getPositionDescription());
- } else {
- //Log.i(TAG, "Got feature " + fname);
- FeatureInfo fi = new FeatureInfo();
- fi.name = fname;
- mAvailableFeatures.put(fname, fi);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
- } else {
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
-
- }
- permReader.close();
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "Got execption parsing permissions.", e);
- } catch (IOException e) {
- Slog.w(TAG, "Got execption parsing permissions.", e);
- }
- }
-
- void readPermission(XmlPullParser parser, String name)
- throws IOException, XmlPullParserException {
-
- name = name.intern();
-
- BasePermission bp = mSettings.mPermissions.get(name);
- if (bp == null) {
- bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN);
- mSettings.mPermissions.put(name, bp);
- }
- int outerDepth = parser.getDepth();
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if ("group".equals(tagName)) {
- String gidStr = parser.getAttributeValue(null, "gid");
- if (gidStr != null) {
- int gid = Process.getGidForName(gidStr);
- bp.gids = appendInt(bp.gids, gid);
- } else {
- Slog.w(TAG, "<group> without gid at "
- + parser.getPositionDescription());
- }
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
static int[] appendInts(int[] cur, int[] add) {
if (add == null) return cur;
if (cur == null) return add;
@@ -3591,7 +3415,6 @@
String className;
if (targetUserId == UserHandle.USER_OWNER) {
className = FORWARD_INTENT_TO_USER_OWNER;
- forwardingResolveInfo.showTargetUserIcon = true;
} else {
className = FORWARD_INTENT_TO_MANAGED_PROFILE;
}
@@ -3599,6 +3422,10 @@
mAndroidApplication.packageName, className);
ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
sourceUserId);
+ if (targetUserId == UserHandle.USER_OWNER) {
+ forwardingActivityInfo.showUserIcon = UserHandle.USER_OWNER;
+ forwardingResolveInfo.noResourceId = true;
+ }
forwardingResolveInfo.activityInfo = forwardingActivityInfo;
forwardingResolveInfo.priority = 0;
forwardingResolveInfo.preferredOrder = 0;
@@ -5512,8 +5339,9 @@
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
// TODO: extend to extract native code from split APKs
- final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
+ ApkHandle handle = null;
try {
+ handle = ApkHandle.create(scanFile.getPath());
// Enable gross and lame hacks for apps that are built with old
// SDK tools. We must scan their APKs for renderscript bitcode and
// not launch them if it's present. Don't bother checking on devices
@@ -5628,7 +5456,7 @@
} catch (IOException ioe) {
Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
} finally {
- handle.close();
+ IoUtils.closeQuietly(handle);
}
}
@@ -7027,7 +6855,11 @@
res.isDefault = info.hasDefault;
res.labelRes = info.labelRes;
res.nonLocalizedLabel = info.nonLocalizedLabel;
- res.icon = info.icon;
+ if (userNeedsBadging(userId)) {
+ res.noResourceId = true;
+ } else {
+ res.icon = info.icon;
+ }
res.system = isSystemApp(res.activityInfo.applicationInfo);
return res;
}
@@ -8219,17 +8051,22 @@
*
* @return true if verification should be performed
*/
- private boolean isVerificationEnabled(int flags) {
+ private boolean isVerificationEnabled(int userId, int flags) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
+ boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
+
// Check if installing from ADB
if ((flags & PackageManager.INSTALL_FROM_ADB) != 0) {
// Do not run verification in a test harness environment
if (ActivityManager.isRunningInTestHarness()) {
return false;
}
+ if (ensureVerifyAppsEnabled) {
+ return true;
+ }
// Check if the developer does not want package verification for ADB installs
if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {
@@ -8237,6 +8074,10 @@
}
}
+ if (ensureVerifyAppsEnabled) {
+ return true;
+ }
+
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
}
@@ -8807,7 +8648,7 @@
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, userIdentifier);
- if (requiredUid != -1 && isVerificationEnabled(flags)) {
+ if (requiredUid != -1 && isVerificationEnabled(userIdentifier, flags)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
@@ -9340,10 +9181,11 @@
nativeLibraryFile.delete();
}
- final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile);
String[] abiList = (abiOverride != null) ?
new String[] { abiOverride } : Build.SUPPORTED_ABIS;
+ ApkHandle handle = null;
try {
+ handle = ApkHandle.create(codeFile);
if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
abiOverride == null &&
NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
@@ -9358,7 +9200,7 @@
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
- handle.close();
+ IoUtils.closeQuietly(handle);
}
return ret;
@@ -13021,23 +12863,30 @@
final File newNativeDir = new File(newNativePath);
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
- // NOTE: We do not report any errors from the APK scan and library
- // copy at this point.
- NativeLibraryHelper.ApkHandle handle =
- new NativeLibraryHelper.ApkHandle(newCodePath);
- final int abi = NativeLibraryHelper.findSupportedAbi(
- handle, Build.SUPPORTED_ABIS);
- if (abi >= 0) {
- NativeLibraryHelper.copyNativeBinariesIfNeededLI(
- handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+ ApkHandle handle = null;
+ try {
+ handle = ApkHandle.create(newCodePath);
+ final int abi = NativeLibraryHelper.findSupportedAbi(
+ handle, Build.SUPPORTED_ABIS);
+ if (abi >= 0) {
+ NativeLibraryHelper.copyNativeBinariesIfNeededLI(
+ handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+ }
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Unable to extract native libs for package :"
+ + mp.packageName, ioe);
+ returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
}
- handle.close();
}
final int[] users = sUserManager.getUserIds();
- for (int user : users) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.packageName,
- newNativePath, user) < 0) {
- returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ for (int user : users) {
+ if (mInstaller.linkNativeLibraryDirectory(pkg.packageName,
+ newNativePath, user) < 0) {
+ returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ }
}
}
@@ -13155,6 +13004,7 @@
// other disk I/O going on, that we'll let it slide for now.
mInstaller.removeUserDataDirs(userHandle);
}
+ mUserNeedsBadging.delete(userHandle);
}
/** Called by UserManagerService */
@@ -13229,4 +13079,26 @@
public IPackageInstaller getPackageInstaller() {
return mInstallerService;
}
+
+ private boolean userNeedsBadging(int userId) {
+ int index = mUserNeedsBadging.indexOfKey(userId);
+ if (index < 0) {
+ final UserInfo userInfo;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ userInfo = sUserManager.getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ final boolean b;
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ b = true;
+ } else {
+ b = false;
+ }
+ mUserNeedsBadging.put(userId, b);
+ return b;
+ }
+ return mUserNeedsBadging.valueAt(index);
+ }
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c78249b..81302b9 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -346,31 +346,21 @@
*/
public static boolean assignSeinfoValue(PackageParser.Package pkg) {
- /*
- * Non system installed apps should be treated the same. This
- * means that any post-loaded apk will be assigned the default
- * tag, if one exists in the policy, else null, without respect
- * to the signing key.
- */
- if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
- ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
+ // We just want one of the signatures to match.
+ for (Signature s : pkg.mSignatures) {
+ if (s == null)
+ continue;
- // We just want one of the signatures to match.
- for (Signature s : pkg.mSignatures) {
- if (s == null)
- continue;
+ Policy policy = sSigSeinfo.get(s);
+ if (policy != null) {
+ String seinfo = policy.checkPolicy(pkg.packageName);
+ if (seinfo != null) {
+ pkg.applicationInfo.seinfo = seinfo;
+ if (DEBUG_POLICY_INSTALL)
+ Slog.i(TAG, "package (" + pkg.packageName +
+ ") labeled with seinfo=" + seinfo);
- Policy policy = sSigSeinfo.get(s);
- if (policy != null) {
- String seinfo = policy.checkPolicy(pkg.packageName);
- if (seinfo != null) {
- pkg.applicationInfo.seinfo = seinfo;
- if (DEBUG_POLICY_INSTALL)
- Slog.i(TAG, "package (" + pkg.packageName +
- ") labeled with seinfo=" + seinfo);
-
- return true;
- }
+ return true;
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 544e93b..194e85d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -838,6 +838,7 @@
writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
serializer.endTag(null, TAG_RESTRICTIONS);
}
@@ -980,6 +981,7 @@
readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
}
private void readBoolean(XmlPullParser parser, Bundle restrictions,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b4cf2ae..f2703ad 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -189,13 +189,14 @@
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
- Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
+ Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
+ Surface outSurface) {
if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
int res = mService.relayoutWindow(this, window, seq, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
- outConfig, outSurface);
+ outStableInsets, outConfig, outSurface);
if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
return res;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1d85723..771b53b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2835,7 +2835,8 @@
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
- Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
+ Rect outVisibleInsets, Rect outStableInsets, Configuration outConfig,
+ Surface outSurface) {
boolean toBeDisplayed = false;
boolean inTouchMode;
boolean configChanged;
@@ -3112,6 +3113,7 @@
outOverscanInsets.set(win.mOverscanInsets);
outContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
+ outStableInsets.set(win.mStableInsets);
if (localLOGV) Slog.v(
TAG, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
@@ -8878,6 +8880,8 @@
+ " " + w.mContentInsets.toShortString()
+ " visibleInsetsChanged=" + w.mVisibleInsetsChanged
+ " " + w.mVisibleInsets.toShortString()
+ + " stableInsetsChanged=" + w.mStableInsetsChanged
+ + " " + w.mStableInsets.toShortString()
+ " surfaceResized=" + winAnimator.mSurfaceResized
+ " configChanged=" + configChanged);
}
@@ -8885,6 +8889,7 @@
w.mLastOverscanInsets.set(w.mOverscanInsets);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
+ w.mLastStableInsets.set(w.mStableInsets);
makeWindowFreezingScreenIfNeededLocked(w);
// If the orientation is changing, then we need to
// hold off on unfreezing the display until this
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7003c8c..23ab73c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -167,6 +167,14 @@
boolean mOverscanInsetsChanged;
/**
+ * Insets that determine the area covered by the stable system windows. These are in the
+ * application's coordinate space (without compatibility scale applied).
+ */
+ final Rect mStableInsets = new Rect();
+ final Rect mLastStableInsets = new Rect();
+ boolean mStableInsetsChanged;
+
+ /**
* Set to true if we are waiting for this window to receive its
* given internal insets before laying out other windows based on it.
*/
@@ -225,6 +233,7 @@
final Rect mParentFrame = new Rect();
final Rect mVisibleFrame = new Rect();
final Rect mDecorFrame = new Rect();
+ final Rect mStableFrame = new Rect();
boolean mContentChanged;
@@ -470,7 +479,7 @@
}
@Override
- public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf) {
+ public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf) {
mHaveFrame = true;
TaskStack stack = mAppToken != null ? getStack() : null;
@@ -537,6 +546,7 @@
mContentFrame.set(cf);
mVisibleFrame.set(vf);
mDecorFrame.set(dcf);
+ mStableFrame.set(sf);
final int fw = mFrame.width();
final int fh = mFrame.height();
@@ -574,6 +584,11 @@
Math.min(mVisibleFrame.right, mFrame.right),
Math.min(mVisibleFrame.bottom, mFrame.bottom));
+ mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
+ Math.max(mStableFrame.top, mFrame.top),
+ Math.min(mStableFrame.right, mFrame.right),
+ Math.min(mStableFrame.bottom, mFrame.bottom));
+
mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0),
Math.max(mOverscanFrame.top - mFrame.top, 0),
Math.max(mFrame.right - mOverscanFrame.right, 0),
@@ -589,6 +604,11 @@
mFrame.right - mVisibleFrame.right,
mFrame.bottom - mVisibleFrame.bottom);
+ mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
+ Math.max(mStableFrame.top - mFrame.top, 0),
+ Math.max(mFrame.right - mStableFrame.right, 0),
+ Math.max(mFrame.bottom - mStableFrame.bottom, 0));
+
mCompatFrame.set(mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
@@ -597,6 +617,7 @@
mOverscanInsets.scale(mInvGlobalScale);
mContentInsets.scale(mInvGlobalScale);
mVisibleInsets.scale(mInvGlobalScale);
+ mStableInsets.scale(mInvGlobalScale);
// Also the scaled frame that we report to the app needs to be
// adjusted to be in its coordinate space.
@@ -618,7 +639,8 @@
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ "): frame=" + mFrame.toShortString()
+ " ci=" + mContentInsets.toShortString()
- + " vi=" + mVisibleInsets.toShortString());
+ + " vi=" + mVisibleInsets.toShortString()
+ + " vi=" + mStableInsets.toShortString());
}
@Override
@@ -724,6 +746,7 @@
mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
+ mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged;
}
@@ -1344,6 +1367,7 @@
final Rect overscanInsets = mLastOverscanInsets;
final Rect contentInsets = mLastContentInsets;
final Rect visibleInsets = mLastVisibleInsets;
+ final Rect stableInsets = mLastStableInsets;
final boolean reportDraw = mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING;
final Configuration newConfig = configChanged ? mConfiguration : null;
if (mClient instanceof IWindow.Stub) {
@@ -1353,15 +1377,15 @@
public void run() {
try {
mClient.resized(frame, overscanInsets, contentInsets,
- visibleInsets, reportDraw, newConfig);
+ visibleInsets, stableInsets, reportDraw, newConfig);
} catch (RemoteException e) {
// Not a remote call, RemoteException won't be raised.
}
}
});
} else {
- mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw,
- newConfig);
+ mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
+ reportDraw, newConfig);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -1373,6 +1397,7 @@
mOverscanInsetsChanged = false;
mContentInsetsChanged = false;
mVisibleInsetsChanged = false;
+ mStableInsetsChanged = false;
mWinAnimator.mSurfaceResized = false;
} catch (RemoteException e) {
mOrientationChanging = false;
@@ -1522,11 +1547,13 @@
mOverscanInsets.printShortString(pw);
pw.print(" content="); mContentInsets.printShortString(pw);
pw.print(" visible="); mVisibleInsets.printShortString(pw);
+ pw.print(" stable="); mStableInsets.printShortString(pw);
pw.println();
pw.print(prefix); pw.print("Lst insets: overscan=");
mLastOverscanInsets.printShortString(pw);
pw.print(" content="); mLastContentInsets.printShortString(pw);
pw.print(" visible="); mLastVisibleInsets.printShortString(pw);
+ pw.print(" stable="); mLastStableInsets.printShortString(pw);
pw.println();
}
pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0d05c5f..52ca098 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -404,6 +404,9 @@
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
try {
+ Slog.i(TAG, "Reading configuration...");
+ SystemConfig.getInstance();
+
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 39f228f..0575a5e9 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -119,7 +119,6 @@
final UserState userState;
synchronized (mLock) {
userState = getCurrentUserStateLocked();
- userState.updateIfNeededLocked();
}
// This is the first time we switch to this user after boot, so
// now is the time to remove obsolete print jobs since they
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index f647814..33edb11 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -126,7 +126,8 @@
mSpooler = new RemotePrintSpooler(context, userId, this);
mHandler = new UserStateHandler(context.getMainLooper());
synchronized (mLock) {
- enableSystemPrintServicesOnFirstBootLocked();
+ enableSystemPrintServicesLocked();
+ onConfigurationChangedLocked();
}
}
@@ -727,7 +728,7 @@
}
}
- private void enableSystemPrintServicesOnFirstBootLocked() {
+ private void enableSystemPrintServicesLocked() {
// Load enabled and installed services.
readEnabledPrintServicesLocked();
readInstalledPrintServicesLocked();
@@ -1159,6 +1160,7 @@
Log.w(LOG_TAG, "Not destroying - session destroyed");
return;
}
+ mIsDestroyed = true;
// Make sure printer tracking is stopped.
final int printerCount = mStateTrackedPrinters.size();
for (int i = 0; i < printerCount; i++) {
diff --git a/telecomm/java/android/telecomm/CallFeatures.java b/telecomm/java/android/telecomm/CallFeatures.java
new file mode 100644
index 0000000..076d25a
--- /dev/null
+++ b/telecomm/java/android/telecomm/CallFeatures.java
@@ -0,0 +1,29 @@
+/*
+ * 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.telecomm;
+
+/**
+ * Defines features of a call. These features represent properties of a call for which it may be
+ * desirable to display an in-call indicator.
+ */
+public final class CallFeatures {
+ private CallFeatures() {};
+
+ public static final int NONE = 0x0;
+ public static final int VoLTE = 0x1;
+ public static final int VoWIFI = 0x2;
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index d2caca0..44380e2 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -408,4 +408,16 @@
public abstract void splitFromConference(String callId);
public void onPostDialContinue(String callId, boolean proceed) {}
+
+ public void onPostDialWait(Connection conn, String remaining) {}
+
+ /**
+ * Called when changes to the features of a call occurs. Features are defined in
+ * {@link android.telecomm.CallFeatures}. The active features for the call are represented as
+ * bits in the features bit-mask.
+ *
+ * @param callId The call to set the features for.
+ * @param features The new features of the call.
+ */
+ public abstract void onFeaturesChanged(String callId, int features);
}
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index 8412e80..1b3c0a3 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -354,4 +354,22 @@
}
}
}
+
+ /**
+ * Set the features associated with the given call.
+ * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+ * bit-mask.
+ *
+ * @param callId The unique ID of the call to set features for.
+ * @param features The features.
+ */
+ public void setFeatures(String callId, int features) {
+ Log.v(this, "setFeatures: %d", features);
+ for (ICallServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setFeatures(callId, features);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java
index 9e3c48d..76b28fa 100644
--- a/telecomm/java/android/telecomm/CallVideoClient.java
+++ b/telecomm/java/android/telecomm/CallVideoClient.java
@@ -17,6 +17,7 @@
package android.telecomm;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -66,22 +67,22 @@
*/
public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
- private static final int MSG_ON_RECEIVE_SESSION_MODIFY_REQUEST = 1;
- private static final int MSG_ON_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
- private static final int MSG_ON_CALL_SESSION_EVENT = 3;
- private static final int MSG_ON_UPDATED_PEER_DIMENSIONS = 4;
- private static final int MSG_ON_UPDATE_CALL_DATA_USAGE = 5;
- private static final int MSG_ON_CAMERA_CAPABILITIES_CHANGE = 6;
+ private static final int MSG_RECEIVE_SESSION_MODIFY_REQUEST = 1;
+ private static final int MSG_RECEIVE_SESSION_MODIFY_RESPONSE = 2;
+ private static final int MSG_HANDLE_CALL_SESSION_EVENT = 3;
+ private static final int MSG_UPDATE_PEER_DIMENSIONS = 4;
+ private static final int MSG_UPDATE_CALL_DATA_USAGE = 5;
+ private static final int MSG_HANDLE_CAMERA_CAPABILITIES_CHANGE = 6;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_ON_RECEIVE_SESSION_MODIFY_REQUEST:
+ case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
onReceiveSessionModifyRequest((VideoCallProfile) msg.obj);
break;
- case MSG_ON_RECEIVE_SESSION_MODIFY_RESPONSE: {
+ case MSG_RECEIVE_SESSION_MODIFY_RESPONSE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
int status = (int) args.arg1;
@@ -95,25 +96,25 @@
}
break;
}
- case MSG_ON_CALL_SESSION_EVENT:
- onCallSessionEvent((int) msg.obj);
+ case MSG_HANDLE_CALL_SESSION_EVENT:
+ onHandleCallSessionEvent((int) msg.obj);
break;
- case MSG_ON_UPDATED_PEER_DIMENSIONS: {
+ case MSG_UPDATE_PEER_DIMENSIONS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
int width = (int) args.arg1;
int height = (int) args.arg2;
- onUpdatedPeerDimensions(width, height);
+ onUpdatePeerDimensions(width, height);
} finally {
args.recycle();
}
break;
}
- case MSG_ON_UPDATE_CALL_DATA_USAGE:
+ case MSG_UPDATE_CALL_DATA_USAGE:
onUpdateCallDataUsage(msg.arg1);
break;
- case MSG_ON_CAMERA_CAPABILITIES_CHANGE:
- onCameraCapabilitiesChange((CallCameraCapabilities) msg.obj);
+ case MSG_HANDLE_CAMERA_CAPABILITIES_CHANGE:
+ onHandleCameraCapabilitiesChange((CallCameraCapabilities) msg.obj);
break;
default:
break;
@@ -126,42 +127,42 @@
*/
private final class CallVideoClientBinder extends ICallVideoClient.Stub {
@Override
- public void onReceiveSessionModifyRequest(VideoCallProfile videoCallProfile) {
- mHandler.obtainMessage(MSG_ON_RECEIVE_SESSION_MODIFY_REQUEST,
+ public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile) {
+ mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_REQUEST,
videoCallProfile).sendToTarget();
}
@Override
- public void onReceiveSessionModifyResponse(int status,
+ public void receiveSessionModifyResponse(int status,
VideoCallProfile requestProfile, VideoCallProfile responseProfile) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = status;
args.arg2 = requestProfile;
args.arg3 = responseProfile;
- mHandler.obtainMessage(MSG_ON_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
+ mHandler.obtainMessage(MSG_RECEIVE_SESSION_MODIFY_RESPONSE, args).sendToTarget();
}
@Override
- public void onCallSessionEvent(int event) {
- mHandler.obtainMessage(MSG_ON_CALL_SESSION_EVENT, event).sendToTarget();
+ public void handleCallSessionEvent(int event) {
+ mHandler.obtainMessage(MSG_HANDLE_CALL_SESSION_EVENT, event).sendToTarget();
}
@Override
- public void onUpdatedPeerDimensions(int width, int height) {
+ public void updatePeerDimensions(int width, int height) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = width;
args.arg2 = height;
- mHandler.obtainMessage(MSG_ON_UPDATED_PEER_DIMENSIONS, args).sendToTarget();
+ mHandler.obtainMessage(MSG_UPDATE_PEER_DIMENSIONS, args).sendToTarget();
}
@Override
- public void onUpdateCallDataUsage(int dataUsage) {
- mHandler.obtainMessage(MSG_ON_UPDATE_CALL_DATA_USAGE, dataUsage).sendToTarget();
+ public void updateCallDataUsage(int dataUsage) {
+ mHandler.obtainMessage(MSG_UPDATE_CALL_DATA_USAGE, dataUsage).sendToTarget();
}
@Override
- public void onCameraCapabilitiesChange(CallCameraCapabilities cameraCapabilities) {
- mHandler.obtainMessage(MSG_ON_CAMERA_CAPABILITIES_CHANGE,
+ public void handleCameraCapabilitiesChange(CallCameraCapabilities cameraCapabilities) {
+ mHandler.obtainMessage(MSG_HANDLE_CAMERA_CAPABILITIES_CHANGE,
cameraCapabilities).sendToTarget();
}
}
@@ -173,21 +174,19 @@
}
/**
- * Retrieves the binder.
- *
- * @return The binder.
+ * Returns binder object which can be used across IPC methods.
* @hide
*/
- public final CallVideoClientBinder getBinder() {
+ public final IBinder getBinder() {
return mBinder;
}
/**
* Called when a session modification request is received from the remote device.
- * The remote request is sent via {@link CallVideoProvider#sendSessionModifyRequest}.
+ * The remote request is sent via {@link CallVideoProvider#onSendSessionModifyRequest}.
* The InCall UI is responsible for potentially prompting the user whether they wish to accept
* the new call profile (e.g. prompt user if they wish to accept an upgrade from an audio to a
- * video call) and should call {@link CallVideoProvider#sendSessionModifyResponse} to indicate
+ * video call) and should call {@link CallVideoProvider#onSendSessionModifyResponse} to indicate
* the video settings the user has agreed to.
*
* @param videoCallProfile The requested video call profile.
@@ -197,7 +196,7 @@
/**
* Called when a response to a session modification request is received from the remote device.
* The remote InCall UI sends the response using
- * {@link CallVideoProvider#sendSessionModifyResponse}.
+ * {@link CallVideoProvider#onSendSessionModifyResponse}.
*
* @param status Status of the session modify request. Valid values are
* {@link CallVideoClient#SESSION_MODIFY_REQUEST_SUCCESS},
@@ -219,7 +218,7 @@
*
* @param event The event.
*/
- public abstract void onCallSessionEvent(int event);
+ public abstract void onHandleCallSessionEvent(int event);
/**
* Handles a change to the video dimensions from the remote caller (peer). This could happen
@@ -228,7 +227,7 @@
* @param width The updated peer video width.
* @param height The updated peer video height.
*/
- public abstract void onUpdatedPeerDimensions(int width, int height);
+ public abstract void onUpdatePeerDimensions(int width, int height);
/**
* Handles an update to the total data used for the current session.
@@ -242,6 +241,6 @@
*
* @param callCameraCapabilities The changed camera capabilities.
*/
- public abstract void onCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities);
+ public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities);
}
diff --git a/telecomm/java/android/telecomm/CallVideoProvider.java b/telecomm/java/android/telecomm/CallVideoProvider.java
index 2f16112..2a04724 100644
--- a/telecomm/java/android/telecomm/CallVideoProvider.java
+++ b/telecomm/java/android/telecomm/CallVideoProvider.java
@@ -17,22 +17,26 @@
package android.telecomm;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteException;
import android.view.Surface;
+import com.android.internal.telecomm.ICallVideoClient;
import com.android.internal.telecomm.ICallVideoProvider;
public abstract class CallVideoProvider {
- private static final int MSG_SET_CAMERA = 1;
- private static final int MSG_SET_PREVIEW_SURFACE = 2;
- private static final int MSG_SET_DISPLAY_SURFACE = 3;
- private static final int MSG_SET_DEVICE_ORIENTATION = 4;
- private static final int MSG_SET_ZOOM = 5;
- private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 6;
- private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 7;
- private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 8;
- private static final int MSG_REQUEST_CALL_DATA_USAGE = 9;
- private static final int MSG_SET_PAUSE_IMAGE = 10;
+ private static final int MSG_SET_CALL_VIDEO_CLIENT = 1;
+ private static final int MSG_SET_CAMERA = 2;
+ private static final int MSG_SET_PREVIEW_SURFACE = 3;
+ private static final int MSG_SET_DISPLAY_SURFACE = 4;
+ private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+ private static final int MSG_SET_ZOOM = 6;
+ private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+ private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+ private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+ private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+ private static final int MSG_SET_PAUSE_IMAGE = 11;
/**
* Default handler used to consolidate binder method calls onto a single thread.
@@ -41,35 +45,45 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_SET_CALL_VIDEO_CLIENT:
+ try {
+ ICallVideoClient callVideoClient =
+ ICallVideoClient.Stub.asInterface((IBinder) msg.obj);
+ RemoteCallVideoClient remoteCallVideoClient =
+ new RemoteCallVideoClient(callVideoClient);
+ onSetCallVideoClient(remoteCallVideoClient);
+ } catch (RemoteException ignored) {
+ }
+ break;
case MSG_SET_CAMERA:
- setCamera((String) msg.obj);
+ onSetCamera((String) msg.obj);
break;
case MSG_SET_PREVIEW_SURFACE:
- setPreviewSurface((Surface) msg.obj);
+ onSetPreviewSurface((Surface) msg.obj);
break;
case MSG_SET_DISPLAY_SURFACE:
- setDisplaySurface((Surface) msg.obj);
+ onSetDisplaySurface((Surface) msg.obj);
break;
case MSG_SET_DEVICE_ORIENTATION:
- setDeviceOrientation(msg.arg1);
+ onSetDeviceOrientation(msg.arg1);
break;
case MSG_SET_ZOOM:
- setZoom((Float) msg.obj);
+ onSetZoom((Float) msg.obj);
break;
case MSG_SEND_SESSION_MODIFY_REQUEST:
- sendSessionModifyRequest((VideoCallProfile) msg.obj);
+ onSendSessionModifyRequest((VideoCallProfile) msg.obj);
break;
case MSG_SEND_SESSION_MODIFY_RESPONSE:
- sendSessionModifyResponse((VideoCallProfile) msg.obj);
+ onSendSessionModifyResponse((VideoCallProfile) msg.obj);
break;
case MSG_REQUEST_CAMERA_CAPABILITIES:
- requestCameraCapabilities();
+ onRequestCameraCapabilities();
break;
case MSG_REQUEST_CALL_DATA_USAGE:
- requestCallDataUsage();
+ onRequestCallDataUsage();
break;
case MSG_SET_PAUSE_IMAGE:
- setPauseImage((String) msg.obj);
+ onSetPauseImage((String) msg.obj);
break;
default:
break;
@@ -81,6 +95,11 @@
* Default ICallVideoProvider implementation.
*/
private final class CallVideoProviderBinder extends ICallVideoProvider.Stub {
+ public void setCallVideoClient(IBinder callVideoClientBinder) {
+ mMessageHandler.obtainMessage(
+ MSG_SET_CALL_VIDEO_CLIENT, callVideoClientBinder).sendToTarget();
+ }
+
public void setCamera(String cameraId) {
mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
}
@@ -102,13 +121,13 @@
}
public void sendSessionModifyRequest(VideoCallProfile requestProfile) {
- mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST,
- requestProfile).sendToTarget();
+ mMessageHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
}
public void sendSessionModifyResponse(VideoCallProfile responseProfile) {
- mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_RESPONSE,
- responseProfile).sendToTarget();
+ mMessageHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
}
public void requestCameraCapabilities() {
@@ -140,11 +159,19 @@
}
/**
+ * Sets a remote interface for invoking callback methods in the InCallUI after performing
+ * telephony actions.
+ *
+ * @param callVideoClient The call video client.
+ */
+ public abstract void onSetCallVideoClient(RemoteCallVideoClient callVideoClient);
+
+ /**
* Sets the camera to be used for video recording in a video call.
*
* @param cameraId The id of the camera.
*/
- public abstract void setCamera(String cameraId);
+ public abstract void onSetCamera(String cameraId);
/**
* Sets the surface to be used for displaying a preview of what the user's camera is
@@ -153,14 +180,14 @@
*
* @param surface The surface.
*/
- public abstract void setPreviewSurface(Surface surface);
+ public abstract void onSetPreviewSurface(Surface surface);
/**
* Sets the surface to be used for displaying the video received from the remote device.
*
* @param surface The surface.
*/
- public abstract void setDisplaySurface(Surface surface);
+ public abstract void onSetDisplaySurface(Surface surface);
/**
* Sets the device orientation, in degrees. Assumes that a standard portrait orientation of the
@@ -168,14 +195,14 @@
*
* @param rotation The device orientation, in degrees.
*/
- public abstract void setDeviceOrientation(int rotation);
+ public abstract void onSetDeviceOrientation(int rotation);
/**
* Sets camera zoom ratio.
*
* @param value The camera zoom ratio.
*/
- public abstract void setZoom(float value);
+ public abstract void onSetZoom(float value);
/**
* Issues a request to modify the properties of the current session. The request is sent to
@@ -186,7 +213,7 @@
*
* @param requestProfile The requested call video properties.
*/
- public abstract void sendSessionModifyRequest(VideoCallProfile requestProfile);
+ public abstract void onSendSessionModifyRequest(VideoCallProfile requestProfile);
/**
* Provides a response to a request to change the current call session video
@@ -198,21 +225,21 @@
*
* @param responseProfile The response call video properties.
*/
- public abstract void sendSessionModifyResponse(VideoCallProfile responseProfile);
+ public abstract void onSendSessionModifyResponse(VideoCallProfile responseProfile);
/**
* Issues a request to the video provider to retrieve the camera capabilities.
* Camera capabilities are reported back to the caller via
- * {@link CallVideoClient#onCameraCapabilitiesChange(CallCameraCapabilities)}.
+ * {@link CallVideoClient#onHandleCameraCapabilitiesChange(CallCameraCapabilities)}.
*/
- public abstract void requestCameraCapabilities();
+ public abstract void onRequestCameraCapabilities();
/**
* Issues a request to the video telephony framework to retrieve the cumulative data usage for
* the current call. Data usage is reported back to the caller via
* {@link CallVideoClient#onUpdateCallDataUsage}.
*/
- public abstract void requestCallDataUsage();
+ public abstract void onRequestCallDataUsage();
/**
* Provides the video telephony framework with the URI of an image to be displayed to remote
@@ -220,5 +247,5 @@
*
* @param uri URI of image to display.
*/
- public abstract void setPauseImage(String uri);
+ public abstract void onSetPauseImage(String uri);
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index f80e4a9..73aac2c 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -34,6 +34,7 @@
/** @hide */
public interface Listener {
void onStateChanged(Connection c, int state);
+ void onFeaturesChanged(Connection c, int features);
void onAudioStateChanged(Connection c, CallAudioState state);
void onHandleChanged(Connection c, Uri newHandle);
void onSignalChanged(Connection c, Bundle details);
@@ -51,6 +52,11 @@
@Override
public void onStateChanged(Connection c, int state) {}
+ /** {@inheritDoc} */
+ @Override
+ public void onFeaturesChanged(Connection c, int features) {}
+
+ /** {@inheritDoc} */
@Override
public void onAudioStateChanged(Connection c, CallAudioState state) {}
@@ -97,6 +103,7 @@
private final List<Connection> mChildConnections = new ArrayList<>();
private int mState = State.NEW;
+ private int mFeatures = CallFeatures.NONE;
private CallAudioState mCallAudioState;
private Uri mHandle;
private boolean mRequestingRingback = false;
@@ -131,6 +138,12 @@
}
/**
+ * @return The features of the call. These are items for which the InCall UI may wish to
+ * display a visual indicator.
+ */
+ public final int getFeatures() { return mFeatures; }
+
+ /**
* @return The audio state of the call, describing how its audio is currently
* being routed by the system. This is {@code null} if this Connection
* does not directly know about its audio state.
@@ -284,6 +297,22 @@
}
/**
+ * Set the features applicable to the connection. These are items for which the InCall UI may
+ * wish to display a visual indicator.
+ * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+ * bit-mask.
+ *
+ * @param features The features active.
+ */
+ public final void setFeatures(int features) {
+ Log.d(this, "setFeatures %d", features);
+ this.mFeatures = features;
+ for (Listener l : mListeners) {
+ l.onFeaturesChanged(this, mFeatures);
+ }
+ }
+
+ /**
* Inform this Connection that the state of its audio output has been changed externally.
*
* @param state The new audio state.
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index e68efea..530e977 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -108,6 +108,14 @@
}
}
+ /** {@inheritDoc} */
+ @Override
+ public void onFeaturesChanged(Connection c, int features) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "Adapter set features %d", features);
+ getAdapter().setFeatures(id, features);
+ }
+
@Override
public void onDisconnected(Connection c, int cause, String message) {
String id = mIdByConnection.get(c);
@@ -561,4 +569,18 @@
Log.w(this, "%s - Cannot find Connection %s", action, callId);
return NULL_CONNECTION;
}
+
+ /**
+ * Handles changes to the features of a connection.
+ * Features are defined in {@link android.telecomm.CallFeatures} and are passed in as a
+ * bit-mask.
+ *
+ * @param callId The call to set the features for.
+ * @param features The new features of the call.
+ */
+ @Override
+ public final void onFeaturesChanged(String callId, int features) {
+ Log.d(this, "onFeaturesChanged %s %d", callId, features);
+ findConnectionForAction(callId, "onFeaturesChanged").setFeatures(features);
+ }
}
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index cf31cec5..44dd567 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -48,6 +48,7 @@
private RemoteCallVideoProvider mRemoteCallVideoProvider;
private final String mParentCallId;
private final List<String> mChildCallIds;
+ private final int mFeatures;
/** @hide */
public InCallCall(
@@ -65,7 +66,8 @@
CallServiceDescriptor handoffDescriptor,
ICallVideoProvider callVideoProvider,
String parentCallId,
- List<String> childCallIds) {
+ List<String> childCallIds,
+ int features) {
mId = id;
mState = state;
mDisconnectCauseCode = disconnectCauseCode;
@@ -81,6 +83,7 @@
mCallVideoProvider = callVideoProvider;
mParentCallId = parentCallId;
mChildCallIds = childCallIds;
+ mFeatures = features;
}
/** The unique ID of the call. */
@@ -187,6 +190,15 @@
return mChildCallIds;
}
+ /**
+ * The features of this call (e.g. VoLTE, VoWIFI).
+ *
+ * @return Features.
+ */
+ public int getFeatures() {
+ return mFeatures;
+ }
+
/** Responsible for creating InCallCall objects for deserialized Parcels. */
public static final Parcelable.Creator<InCallCall> CREATOR =
new Parcelable.Creator<InCallCall> () {
@@ -211,10 +223,11 @@
String parentCallId = source.readString();
List<String> childCallIds = new ArrayList<>();
source.readList(childCallIds, classLoader);
+ int features = source.readInt();
return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg,
cannedSmsResponses, capabilities, connectTimeMillis, handle, gatewayInfo,
subscription, descriptor, handoffDescriptor, callVideoProvider, parentCallId,
- childCallIds);
+ childCallIds, features);
}
@Override
@@ -248,6 +261,7 @@
mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null);
destination.writeString(mParentCallId);
destination.writeList(mChildCallIds);
+ destination.writeInt(mFeatures);
}
@Override
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoClient.java b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
new file mode 100644
index 0000000..f0a3afc
--- /dev/null
+++ b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
@@ -0,0 +1,65 @@
+/*
+ * 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.telecomm;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telecomm.CallCameraCapabilities;
+import android.telecomm.VideoCallProfile;
+
+import com.android.internal.telecomm.ICallVideoClient;
+
+public class RemoteCallVideoClient implements IBinder.DeathRecipient {
+ private final ICallVideoClient mCallVideoClient;
+
+ RemoteCallVideoClient(ICallVideoClient callVideoProvider) throws RemoteException {
+ mCallVideoClient = callVideoProvider;
+ mCallVideoClient.asBinder().linkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ mCallVideoClient.asBinder().unlinkToDeath(this, 0);
+ }
+
+ public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile)
+ throws RemoteException {
+ mCallVideoClient.receiveSessionModifyRequest(videoCallProfile);
+ }
+
+ public void receiveSessionModifyResponse(int status, VideoCallProfile requestedProfile,
+ VideoCallProfile responseProfile) throws RemoteException {
+ mCallVideoClient.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+ }
+
+ public void handleCallSessionEvent(int event) throws RemoteException {
+ mCallVideoClient.handleCallSessionEvent(event);
+ }
+
+ public void updatePeerDimensions(int width, int height) throws RemoteException {
+ mCallVideoClient.updatePeerDimensions(width, height);
+ }
+
+ public void updateCallDataUsage(int dataUsage) throws RemoteException {
+ mCallVideoClient.updateCallDataUsage(dataUsage);
+ }
+
+ public void handleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities)
+ throws RemoteException {
+ mCallVideoClient.handleCameraCapabilitiesChange(callCameraCapabilities);
+ }
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
index 872085a..856d321 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
@@ -18,10 +18,11 @@
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.Surface;
+import com.android.internal.telecomm.ICallVideoClient;
import com.android.internal.telecomm.ICallVideoProvider;
-
public class RemoteCallVideoProvider implements IBinder.DeathRecipient {
private final ICallVideoProvider mCallVideoProvider;
@@ -35,12 +36,47 @@
mCallVideoProvider.asBinder().unlinkToDeath(this, 0);
}
- /**
- * Sets the camera to be used for video recording in a video call, using the binder.
- *
- * @param cameraId The id of the camera.
- */
+ public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException {
+ mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder());
+ }
+
public void setCamera(String cameraId) throws RemoteException {
mCallVideoProvider.setCamera(cameraId);
}
-}
+
+ public void setPreviewSurface(Surface surface) throws RemoteException {
+ mCallVideoProvider.setPreviewSurface(surface);
+ }
+
+ public void setDisplaySurface(Surface surface) throws RemoteException {
+ mCallVideoProvider.setDisplaySurface(surface);
+ }
+
+ public void setDeviceOrientation(int rotation) throws RemoteException {
+ mCallVideoProvider.setDeviceOrientation(rotation);
+ }
+
+ public void setZoom(float value) throws RemoteException {
+ mCallVideoProvider.setZoom(value);
+ }
+
+ public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException {
+ mCallVideoProvider.sendSessionModifyRequest(requestProfile);
+ }
+
+ public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException {
+ mCallVideoProvider.sendSessionModifyResponse(responseProfile);
+ }
+
+ public void requestCameraCapabilities() throws RemoteException {
+ mCallVideoProvider.requestCameraCapabilities();
+ }
+
+ public void requestCallDataUsage() throws RemoteException {
+ mCallVideoProvider.requestCallDataUsage();
+ }
+
+ public void setPauseImage(String uri) throws RemoteException {
+ mCallVideoProvider.setPauseImage(uri);
+ }
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index a159dee..f200bc0 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -43,7 +43,7 @@
private final String mConnectionId;
private final Set<Listener> mListeners = new HashSet<>();
- private int mState;
+ private int mState = Connection.State.NEW;
private int mDisconnectCause = DisconnectCause.NOT_VALID;
private String mDisconnectMessage;
private boolean mRequestingRingback;
@@ -67,6 +67,10 @@
mListeners.remove(listener);
}
+ public int getState() {
+ return mState;
+ }
+
public int getDisconnectCause() {
return mDisconnectCause;
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 02a100e..f6fc69f 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -193,6 +193,12 @@
} catch (RemoteException e) {
}
}
+
+ /** ${inheritDoc} */
+ @Override
+ public void setFeatures(String connectionId, int features) {
+ // not supported for remote connections.
+ }
};
RemoteConnectionService(ComponentName componentName, ICallService callService)
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index a0abc28..6bb75be 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -1,17 +1,15 @@
/*
* 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
+ * 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
+ * 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.
+ * 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.telecomm;
@@ -62,14 +60,111 @@
@SystemApi
public ComponentName getDefaultPhoneApp() {
try {
- return getTelecommService().getDefaultPhoneApp();
+ if (isServiceConnected()) {
+ return getTelecommService().getDefaultPhoneApp();
+ }
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the default phone app.", e);
}
return null;
}
+ /**
+ * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
+ * states).
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isInAPhoneCall() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecommService().isInAPhoneCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get default phone app.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether there currently exists is a ringing incoming-call.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isRinging() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecommService().isRinging();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get ringing state of phone app.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Ends an ongoing call. TODO(santoscordon): L-release - need to convert all invocations of
+ * ITelephony#endCall to use this method (clockwork & gearhead).
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean endCall() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecommService().endCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#endCall", e);
+ }
+ return false;
+ }
+
+ /**
+ * If there is a ringing incoming call, this method accepts the call on behalf of the user.
+ * TODO(santoscordon): L-release - need to convert all invocation of
+ * ITelephony#answerRingingCall to use this method (clockwork & gearhead).
+ *
+ * @hide
+ */
+ @SystemApi
+ public void acceptRingingCall() {
+ try {
+ if (isServiceConnected()) {
+ getTelecommService().acceptRingingCall();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#acceptRingingCall", e);
+ }
+ }
+
+ /**
+ * Silences the ringer if a ringing call exists.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void silenceRinger() {
+ try {
+ if (isServiceConnected()) {
+ getTelecommService().silenceRinger();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
+ }
+ }
+
private ITelecommService getTelecommService() {
return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
}
+
+ private boolean isServiceConnected() {
+ boolean isConnected = getTelecommService() != null;
+ if (!isConnected) {
+ Log.w(TAG, "Telecomm Service not found.");
+ }
+ return isConnected;
+ }
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index b81ef37..e78762d 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -65,4 +65,6 @@
void queryRemoteConnectionServices(RemoteServiceCallback callback);
void setCallVideoProvider(String callId, ICallVideoProvider callVideoProvider);
+
+ void setFeatures(String callId, int features);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl b/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl
index 0a854a1..2689561 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl
@@ -29,16 +29,16 @@
*/
oneway interface ICallVideoClient {
- void onReceiveSessionModifyRequest(in VideoCallProfile videoCallProfile);
+ void receiveSessionModifyRequest(in VideoCallProfile videoCallProfile);
- void onReceiveSessionModifyResponse(int status, in VideoCallProfile requestedProfile,
+ void receiveSessionModifyResponse(int status, in VideoCallProfile requestedProfile,
in VideoCallProfile responseProfile);
- void onCallSessionEvent(int event);
+ void handleCallSessionEvent(int event);
- void onUpdatedPeerDimensions(int width, int height);
+ void updatePeerDimensions(int width, int height);
- void onUpdateCallDataUsage(int dataUsage);
+ void updateCallDataUsage(int dataUsage);
- void onCameraCapabilitiesChange(in CallCameraCapabilities callCameraCapabilities);
+ void handleCameraCapabilitiesChange(in CallCameraCapabilities callCameraCapabilities);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl b/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
index f6587b7..860a431 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl
@@ -19,12 +19,16 @@
import android.view.Surface;
import android.telecomm.VideoCallProfile;
+import com.android.internal.telecomm.ICallVideoClient;
+
/**
* Internal remote interface for a call video provider.
* @see android.telecomm.CallVideoProvider
* @hide
*/
oneway interface ICallVideoProvider {
+ void setCallVideoClient(IBinder callVideoClient);
+
void setCamera(String cameraId);
void setPreviewSurface(in Surface surface);
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 2ae5768..65389df 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -25,16 +25,6 @@
* {@hide}
*/
interface ITelecommService {
-
- /**
- * Silence the ringer if an incoming call is currently ringing.
- * (If vibrating, stop the vibrator also.)
- *
- * It's safe to call this if the ringer has already been silenced, or
- * even if there's no incoming call. (If so, this method will do nothing.)
- */
- void silenceRinger();
-
/**
* Brings the in-call screen to the foreground if there is an active call.
*
@@ -61,4 +51,33 @@
* Returns the component name of the default phone application.
*/
ComponentName getDefaultPhoneApp();
+
+ //
+ // Internal system apis relating to call management.
+ //
+
+ /**
+ * @see TelecommManager#silenceRinger
+ */
+ void silenceRinger();
+
+ /**
+ * @see TelecommManager#isInAPhoneCall
+ */
+ boolean isInAPhoneCall();
+
+ /**
+ * @see TelecomManager#isRinging
+ */
+ boolean isRinging();
+
+ /**
+ * @see TelecommManager#endCall
+ */
+ boolean endCall();
+
+ /**
+ * @see TelecommManager#acceptRingingCall
+ */
+ void acceptRingingCall();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 124a8ec..0e5b0e6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2793,6 +2793,25 @@
}
/**
+ * Set the CDMA subscription source.
+ * Used for device supporting both NV and RUIM for CDMA.
+ *
+ * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ public boolean setCdmaSubscription(int subscriptionType) {
+ try {
+ return getITelephony().setCdmaSubscription(subscriptionType);
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setCdmaSubscription RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setCdmaSubscription NPE", ex);
+ }
+ return false;
+ }
+
+ /**
* Expose the rest of ITelephony to @SystemApi
*/
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index 32929ed..f9375e4 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -47,6 +47,26 @@
int queryCallWaiting();
/**
+ * Retrieves the default CLIR setting.
+ */
+ int queryCLIR();
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ int queryCLIP();
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ int queryCOLR();
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ int queryCOLP();
+
+ /**
* Updates or retrieves the supplementary service configuration.
*/
int transact(in Bundle ssInfo);
@@ -67,6 +87,26 @@
int updateCallWaiting(boolean enable);
/**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ int updateCLIR(int clirMode);
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ int updateCLIP(boolean enable);
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ int updateCOLR(int presentation);
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ int updateCOLP(boolean enable);
+
+ /**
* Sets the listener.
*/
void setListener(in IImsUtListener listener);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index beee616..237e7f3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -606,6 +606,15 @@
boolean setPreferredNetworkType(int networkType);
/**
+ * Set the CDMA subscription source.
+ * Used for device supporting both NV and RUIM for CDMA.
+ *
+ * @param subscriptionType the subscription type, 0 for RUIM, 1 for NV.
+ * @return true on success; false on any failure.
+ */
+ boolean setCdmaSubscription(int subscriptionType);
+
+ /**
* User enable/disable Mobile Data.
*
* @param enable true to turn on, else false
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 59036da..a14714a 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -34,6 +34,7 @@
import android.content.pm.ManifestDigest;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -744,7 +745,7 @@
/**
* @hide
*/
- public Bitmap getUserIcon(int userId) {
+ public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
throw new UnsupportedOperationException();
}
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
index 2ea5219..1bb6d0c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
@@ -22,6 +22,8 @@
import android.content.pm.ConfigurationInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.opengl.EGL14;
+import android.opengl.EGLDisplay;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
@@ -29,7 +31,7 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
-
+import android.view.MotionEvent;
import java.io.IOException;
import java.io.InputStream;
@@ -54,7 +56,8 @@
// 2.0-compatible
// context, and set an OpenGL ES 2.0-compatible renderer.
mGLSurfaceView.setEGLContextClientVersion(2);
- mGLSurfaceView.setRenderer(new GLES20TriangleRenderer(this));
+ mRenderer = new GLES20TriangleRenderer(this);
+ mGLSurfaceView.setRenderer(mRenderer);
} else {
throw new IllegalStateException("Can't find OGL ES2.0 context");
}
@@ -84,7 +87,17 @@
mGLSurfaceView.onPause();
}
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ Log.i("motion", event.toString());
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mRenderer.toggleDepthTest();
+ }
+ return true;
+ }
+
private GLSurfaceView mGLSurfaceView;
+ private GLES20TriangleRenderer mRenderer;
/*
* Copyright (C) 2009 The Android Open Source Project Licensed under the
@@ -99,7 +112,9 @@
*/
class GLES20TriangleRenderer implements GLSurfaceView.Renderer {
-
+ private final static int REPEAT_RECTANGLES = 10;
+ private boolean mDepthTestEnabled = true;
+ private final static int FRAME_REPEAT_TIMES = 1;
public GLES20TriangleRenderer(Context context) {
mContext = context;
mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
@@ -107,39 +122,60 @@
mTriangleVertices.put(mTriangleVerticesData).position(0);
}
+
+ public void toggleDepthTest() {
+ mDepthTestEnabled = !mDepthTestEnabled;
+ Log.v(TAG, "mDepthTestEnabled is " + mDepthTestEnabled);
+ }
+
public void onDrawFrame(GL10 glUnused) {
- // Ignore the passed-in GL10 interface, and use the GLES20
- // class's static methods instead.
- GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
- GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
- GLES20.glUseProgram(mProgram);
- checkGlError("glUseProgram");
+ for (int j = 0 ; j < FRAME_REPEAT_TIMES; j ++) {
+ // Ignore the passed-in GL10 interface, and use the GLES20
+ // class's static methods instead.
+ GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+ if (mDepthTestEnabled) {
+ GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+ } else {
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ }
+ GLES20.glUseProgram(mProgram);
+ if (mDepthTestEnabled) {
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+ } else {
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+ }
+ checkGlError("glUseProgram");
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
- mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
- GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
- TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
- checkGlError("glVertexAttribPointer maPosition");
- mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
- GLES20.glEnableVertexAttribArray(maPositionHandle);
- checkGlError("glEnableVertexAttribArray maPositionHandle");
- GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
- TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
- checkGlError("glVertexAttribPointer maTextureHandle");
- GLES20.glEnableVertexAttribArray(maTextureHandle);
- checkGlError("glEnableVertexAttribArray maTextureHandle");
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+ GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ checkGlError("glVertexAttribPointer maPosition");
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+ GLES20.glEnableVertexAttribArray(maPositionHandle);
+ checkGlError("glEnableVertexAttribArray maPositionHandle");
+ GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ checkGlError("glVertexAttribPointer maTextureHandle");
+ GLES20.glEnableVertexAttribArray(maTextureHandle);
+ checkGlError("glEnableVertexAttribArray maTextureHandle");
- long time = SystemClock.uptimeMillis() % 4000L;
- float angle = 0.090f * ((int) time);
- Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
- Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
- Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
+ for (int i = 0 ; i < REPEAT_RECTANGLES; i ++) {
+ float step = ((float)i) / REPEAT_RECTANGLES;
+ Matrix.setIdentityM(mMMatrix, 0);
+ Matrix.translateM(mMMatrix, 0, 0, step, step / 2);
+ Matrix.scaleM(mMMatrix, 0, 2.0f, 1.0f, 1.0f);
+ Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
+ Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
- GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
- GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
- checkGlError("glDrawArrays");
+ GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
+ GLES20.glUniform4f(muOverlayHandle, step , step, step , step);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+ checkGlError("glDrawArrays");
+ }
+ }
}
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
@@ -174,6 +210,12 @@
throw new RuntimeException("Could not get attrib location for uMVPMatrix");
}
+ muOverlayHandle = GLES20.glGetUniformLocation(mProgram, "uOverlay");
+ checkGlError("glGetUniformLocation uOverlay");
+ if (muOverlayHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for muOverlayHandle");
+ }
+
/*
* Create our texture. This has to be done each time the surface is
* created.
@@ -213,6 +255,10 @@
bitmap.recycle();
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+ EGLDisplay display = EGL14.eglGetCurrentDisplay();
+ EGL14.eglSwapInterval(display, 0);
+
}
private int loadShader(int shaderType, String source) {
@@ -276,9 +322,10 @@
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
private final float[] mTriangleVerticesData = {
// X, Y, Z, U, V
- -1.0f, -0.5f, 0, -0.5f, 0.0f,
- 1.0f, -0.5f, 0, 1.5f, -0.0f,
- 0.0f, 1.11803399f, 0, 0.5f, 1.61803399f };
+ -1.0f, -1.0f, 0, 0.0f, 0.0f,
+ -1.0f, 1.0f, 0, 0.0f, 1.0f,
+ 1.0f, -1.0f, 0, 1.0f, 0.0f,
+ 1.0f, 1.0f, 0, 1.0f, 1.0f, };
private FloatBuffer mTriangleVertices;
@@ -296,8 +343,9 @@
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform sampler2D sTexture;\n" +
+ "uniform vec4 uOverlay;\n" +
"void main() {\n" +
- " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord) * uOverlay;\n" +
"}\n";
private float[] mMVPMatrix = new float[16];
@@ -310,6 +358,7 @@
private int muMVPMatrixHandle;
private int maPositionHandle;
private int maTextureHandle;
+ private int muOverlayHandle;
private Context mContext;
private static final String TAG = "GLES20TriangleRenderer";
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 890214f..613660c13 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -99,7 +99,7 @@
</activity>
<activity
android:name="VectorCheckbox"
- android:label="On a Checkbox" >
+ android:label="Basic static vector drawables" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -115,6 +115,15 @@
<category android:name="com.android.test.dynamic.TEST" />
</intent-filter>
</activity>
+ <activity
+ android:name="ScaleDrawableTests"
+ android:label="Scale Type Test" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="com.android.test.dynamic.TEST" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
new file mode 100644
index 0000000..07ca2eb
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
@@ -0,0 +1,97 @@
+/*
+ * 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.test.dynamic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.GridLayout;
+import android.widget.ScrollView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ScaleDrawableTests extends Activity {
+ private static final String LOGCAT = "VectorDrawable1";
+
+ private String[] scaleTypes = {
+ "MATRIX (0)",
+ "FIT_XY (1)",
+ "FIT_START (2)",
+ "FIT_CENTER (3)",
+ "FIT_END (4)",
+ "CENTER (5)",
+ "CENTER_CROP (6)",
+ "CENTER_INSIDE (7)"
+ };
+
+ protected int[] icon = {
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ R.drawable.bitmap_drawable01,
+ };
+
+ protected int[] vector_icons = {
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ R.drawable.vector_drawable16,
+ };
+
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ScrollView scrollView = new ScrollView(this);
+ GridLayout container = new GridLayout(this);
+ scrollView.addView(container);
+ container.setColumnCount(3);
+ container.setBackgroundColor(0xFF888888);
+
+ LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ params.width = 400;
+ params.height = 300;
+
+ for (int i = 0; i < icon.length; i++) {
+ TextView t = new TextView(this);
+ t.setText(scaleTypes[i]);
+ container.addView(t);
+
+ ImageView png_view = new ImageView(this);
+ png_view.setLayoutParams(params);
+ png_view.setScaleType(ImageView.ScaleType.values()[i]);
+ png_view.setImageResource(icon[i]);
+ container.addView(png_view);
+
+ ImageView view = new ImageView(this);
+ view.setLayoutParams(params);
+ view.setScaleType(ImageView.ScaleType.values()[i]);
+ view.setImageResource(vector_icons[i]);
+ container.addView(view);
+ }
+
+ setContentView(scrollView);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 33813d1..88ebd1f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -28,6 +28,8 @@
import com.ibm.icu.lang.UScript;
import com.ibm.icu.lang.UScriptRun;
+import com.ibm.icu.text.Bidi;
+import com.ibm.icu.text.BidiRun;
import android.graphics.Paint_Delegate.FontInfo;
@@ -38,7 +40,7 @@
@SuppressWarnings("deprecation")
public class BidiRenderer {
- /*package*/ static class ScriptRun {
+ private static class ScriptRun {
int start;
int limit;
boolean isRtl;
@@ -66,7 +68,7 @@
* @param paint The Paint to use to get the fonts. Should not be null.
* @param text Unidirectional text. Should not be null.
*/
- /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+ public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
assert (paint != null);
mGraphics = graphics;
mPaint = paint;
@@ -75,13 +77,45 @@
for (FontInfo fontInfo : paint.getFonts()) {
mFonts.add(fontInfo.mFont);
}
+ mBounds = new RectF();
+ }
+
+ /**
+ *
+ * @param x The x-coordinate of the left edge of where the text should be drawn on the given
+ * graphics.
+ * @param y The y-coordinate at which to draw the text on the given mGraphics.
+ *
+ */
+ public BidiRenderer setRenderLocation(float x, float y) {
+ mBounds = new RectF(x, y, x, y);
+ mBaseline = y;
+ return this;
+ }
+
+ /**
+ * Perform Bidi Analysis on the text and then render it.
+ * <p/>
+ * To skip the analysis and render unidirectional text, see {@link
+ * #renderText(int, int, boolean, float[], int, boolean)}
+ */
+ public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
+ int advancesIndex, boolean draw) {
+ Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
+ for (int i = 0; i < bidi.countRuns(); i++) {
+ BidiRun visualRun = bidi.getVisualRun(i);
+ boolean isRtl = visualRun.getDirection() == Bidi.RTL;
+ renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
+ advancesIndex, draw);
+ }
+ return mBounds;
}
/**
* Render unidirectional text.
- *
+ * <p/>
* This method can also be used to measure the width of the text without actually drawing it.
- *
+ * <p/>
* @param start index of the first character
* @param limit index of the first character that should not be rendered.
* @param isRtl is the text right-to-left
@@ -90,17 +124,12 @@
* @param advancesIndex index into advances from where the advances need to be filled.
* @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics
* at the given co-ordinates
- * @param x The x-coordinate of the left edge of where the text should be drawn on the given
- * graphics.
- * @param y The y-coordinate at which to draw the text on the given mGraphics.
* @return A rectangle specifying the bounds of the text drawn.
*/
- /* package */ RectF renderText(int start, int limit, boolean isRtl, float[] advances,
- int advancesIndex, boolean draw, float x, float y) {
+ public RectF renderText(int start, int limit, boolean isRtl, float[] advances,
+ int advancesIndex, boolean draw) {
// We break the text into scripts and then select font based on it and then render each of
// the script runs.
- mBounds = new RectF(x, y, x, y);
- mBaseline = y;
for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
@@ -244,4 +273,22 @@
}
run.font = fonts.get(0);
}
+
+ private static int getIcuFlags(int bidiFlag) {
+ switch (bidiFlag) {
+ case Paint.BIDI_LTR:
+ case Paint.BIDI_FORCE_LTR:
+ return Bidi.DIRECTION_LEFT_TO_RIGHT;
+ case Paint.BIDI_RTL:
+ case Paint.BIDI_FORCE_RTL:
+ return Bidi.DIRECTION_RIGHT_TO_LEFT;
+ case Paint.BIDI_DEFAULT_LTR:
+ return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+ case Paint.BIDI_DEFAULT_RTL:
+ return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
+ default:
+ assert false;
+ return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+ }
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 7016136..bd88ae2 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -315,7 +315,7 @@
@LayoutlibDelegate
/*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
- int config, int allocSize) {
+ int config, int allocSize, boolean isPremultiplied) {
Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
"Bitmap.reconfigure() is not supported", null /*data*/);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 2ee06fc..7c8ef70 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -55,16 +55,19 @@
private static final DelegateManager<Canvas_Delegate> sManager =
new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
+
// ---- delegate helper data ----
private final static boolean[] sBoolOut = new boolean[1];
+
// ---- delegate data ----
private Bitmap_Delegate mBitmap;
private GcSnapshot mSnapshot;
private DrawFilter_Delegate mDrawFilter = null;
+
// ---- Public Helper methods ----
/**
@@ -100,206 +103,6 @@
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static boolean isOpaque(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return false;
- }
-
- return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
- }
-
- @LayoutlibDelegate
- /*package*/ static int getWidth(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.mBitmap.getImage().getWidth();
- }
-
- @LayoutlibDelegate
- /*package*/ static int getHeight(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.mBitmap.getImage().getHeight();
- }
-
- @LayoutlibDelegate
- /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.getSnapshot().translate(dx, dy);
- }
-
- @LayoutlibDelegate
- /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
- }
-
- @LayoutlibDelegate
- /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.getSnapshot().scale(sx, sy);
- }
-
- @LayoutlibDelegate
- /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return;
- }
-
- // get the current top graphics2D object.
- GcSnapshot g = canvasDelegate.getSnapshot();
-
- // get its current matrix
- AffineTransform currentTx = g.getTransform();
- // get the AffineTransform for the given skew.
- float[] mtx = Matrix_Delegate.getSkew(kx, ky);
- AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
-
- // combine them so that the given matrix is applied after.
- currentTx.preConcatenate(matrixTx);
-
- // give it to the graphics2D as a new matrix replacing all previous transform
- g.setTransform(currentTx);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
- return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
- return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
- (float) rect.right, (float) rect.bottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
- float bottom) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return false;
- }
-
- return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
- int bottom) {
-
- return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static int save(Canvas thisCanvas) {
- return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
- }
-
- @LayoutlibDelegate
- /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.save(saveFlags);
- }
-
- @LayoutlibDelegate
- /*package*/ static void restore(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.restore();
- }
-
- @LayoutlibDelegate
- /*package*/ static int getSaveCount(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.getSnapshot().size();
- }
-
- @LayoutlibDelegate
- /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper());
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.restoreTo(saveCount);
- }
-
- @LayoutlibDelegate
- /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
- Paint paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPoint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPoint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void drawLines(Canvas thisCanvas,
- final float[] pts, final int offset, final int count,
- Paint paint) {
- draw(thisCanvas.getNativeCanvasWrapper(), paint.mNativePaint, false /*compositeOnly*/,
- false /*forceSrcMode*/, new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- for (int i = 0 ; i < count ; i += 4) {
- graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
- (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
- }
- }
- });
- }
-
- @LayoutlibDelegate
/*package*/ static void freeCaches() {
// nothing to be done here.
}
@@ -328,36 +131,73 @@
}
@LayoutlibDelegate
- /*package*/ static void copyNativeCanvasState(long srcCanvas, long dstCanvas) {
+ /*package*/ static long initCanvas(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate srcCanvasDelegate = sManager.getDelegate(srcCanvas);
- if (srcCanvasDelegate == null) {
- return;
+ Canvas_Delegate nativeCanvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (nativeCanvasDelegate == null) {
+ return 0;
}
- // get the delegate from the native int.
- Canvas_Delegate dstCanvasDelegate = sManager.getDelegate(dstCanvas);
- if (dstCanvasDelegate == null) {
- return;
- }
+ Canvas_Delegate newDelegate = new Canvas_Delegate();
+
// TODO: actually copy the canvas state.
+ return sManager.addNewDelegate(newDelegate);
}
@LayoutlibDelegate
- /*package*/ static int native_saveLayer(long nativeCanvas, RectF bounds,
- long paint, int layerFlags) {
+ /*package*/
+ static void native_setBitmap(long canvas, long bitmap, boolean copyState) {
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+ if (canvasDelegate == null || bitmapDelegate==null) {
+ return;
+ }
+ canvasDelegate.mBitmap = bitmapDelegate;
+ canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean native_isOpaque(long nativeCanvas) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return false;
+ }
+
+ return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static int native_getWidth(long nativeCanvas) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
+ return canvasDelegate.mBitmap.getImage().getWidth();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static int native_getHeight(long nativeCanvas) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
return 0;
}
- return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
+ return canvasDelegate.mBitmap.getImage().getHeight();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static int native_save(long nativeCanvas, int saveFlags) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return 0;
+ }
+
+ return canvasDelegate.save(saveFlags);
}
@LayoutlibDelegate
@@ -380,19 +220,6 @@
}
@LayoutlibDelegate
- /*package*/ static int native_saveLayerAlpha(long nativeCanvas,
- RectF bounds, int alpha,
- int layerFlags) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
- }
-
- @LayoutlibDelegate
/*package*/ static int native_saveLayerAlpha(long nativeCanvas, float l,
float t, float r, float b,
int alpha, int layerFlags) {
@@ -405,6 +232,95 @@
return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
}
+ @LayoutlibDelegate
+ /*package*/ static void native_restore(long nativeCanvas) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.restore();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.restoreTo(saveCount);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static int native_getSaveCount(long nativeCanvas) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return 0;
+ }
+
+ return canvasDelegate.getSnapshot().size();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_translate(long nativeCanvas, float dx, float dy) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.getSnapshot().translate(dx, dy);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_scale(long nativeCanvas, float sx, float sy) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.getSnapshot().scale(sx, sy);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_rotate(long nativeCanvas, float degrees) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_skew(long nativeCanvas, float kx, float ky) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ // get the current top graphics2D object.
+ GcSnapshot g = canvasDelegate.getSnapshot();
+
+ // get its current matrix
+ AffineTransform currentTx = g.getTransform();
+ // get the AffineTransform for the given skew.
+ float[] mtx = Matrix_Delegate.getSkew(kx, ky);
+ AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
+
+ // combine them so that the given matrix is applied after.
+ currentTx.preConcatenate(matrixTx);
+
+ // give it to the graphics2D as a new matrix replacing all previous transform
+ g.setTransform(currentTx);
+ }
@LayoutlibDelegate
/*package*/ static void native_concat(long nCanvas, long nMatrix) {
@@ -469,7 +385,6 @@
float left, float top,
float right, float bottom,
int regionOp) {
-
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
if (canvasDelegate == null) {
@@ -568,15 +483,7 @@
}
@LayoutlibDelegate
- /*package*/ static boolean native_quickReject(long nativeCanvas,
- RectF rect) {
- // FIXME properly implement quickReject
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_quickReject(long nativeCanvas,
- long path) {
+ /*package*/ static boolean native_quickReject(long nativeCanvas, long path) {
// FIXME properly implement quickReject
return false;
}
@@ -645,10 +552,25 @@
}
@LayoutlibDelegate
+ /*package*/ static void native_drawPoint(long nativeCanvas, float x, float y,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPoint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPoint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
/*package*/ static void native_drawLine(long nativeCanvas,
final float startX, final float startY, final float stopX, final float stopY,
long paint) {
-
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -659,8 +581,19 @@
}
@LayoutlibDelegate
- /*package*/ static void native_drawRect(long nativeCanvas, RectF rect, long paint) {
- native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
+ /*package*/ static void native_drawLines(long nativeCanvas,
+ final float[] pts, final int offset, final int count,
+ long nativePaint) {
+ draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
+ false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+ @Override
+ public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
+ for (int i = 0; i < count; i += 4) {
+ graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
+ (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
+ }
+ }
+ });
}
@LayoutlibDelegate
@@ -690,8 +623,9 @@
}
@LayoutlibDelegate
- /*package*/ static void native_drawOval(long nativeCanvas, final RectF oval, long paint) {
- if (oval.right > oval.left && oval.bottom > oval.top) {
+ /*package*/ static void native_drawOval(long nativeCanvas, final float left,
+ final float top, final float right, final float bottom, long paint) {
+ if (right > left && bottom > top) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -701,14 +635,14 @@
// draw
if (style == Paint.Style.FILL.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillOval((int)oval.left, (int)oval.top,
- (int)oval.width(), (int)oval.height());
+ graphics.fillOval((int)left, (int)top,
+ (int)(right - left), (int)(bottom - top));
}
if (style == Paint.Style.STROKE.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawOval((int)oval.left, (int)oval.top,
- (int)oval.width(), (int)oval.height());
+ graphics.drawOval((int)left, (int)top,
+ (int)(right - left), (int)(bottom - top));
}
}
});
@@ -719,15 +653,16 @@
/*package*/ static void native_drawCircle(long nativeCanvas,
float cx, float cy, float radius, long paint) {
native_drawOval(nativeCanvas,
- new RectF(cx - radius, cy - radius, cx + radius, cy + radius),
+ cx - radius, cy - radius, cx + radius, cy + radius,
paint);
}
@LayoutlibDelegate
/*package*/ static void native_drawArc(long nativeCanvas,
- final RectF oval, final float startAngle, final float sweep,
+ final float left, final float top, final float right, final float bottom,
+ final float startAngle, final float sweep,
final boolean useCenter, long paint) {
- if (oval.right > oval.left && oval.bottom > oval.top) {
+ if (right > left && bottom > top) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -735,7 +670,7 @@
int style = paintDelegate.getStyle();
Arc2D.Float arc = new Arc2D.Float(
- oval.left, oval.top, oval.width(), oval.height(),
+ left, top, right - left, bottom - top,
-startAngle, -sweep,
useCenter ? Arc2D.PIE : Arc2D.OPEN);
@@ -758,7 +693,6 @@
/*package*/ static void native_drawRoundRect(long nativeCanvas,
final float left, final float top, final float right, final float bottom,
final float rx, final float ry, long paint) {
-
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -890,7 +824,6 @@
final float y, int width, int height,
boolean hasAlpha,
long nativePaintOrZero) {
-
// create a temp BufferedImage containing the content.
final BufferedImage image = new BufferedImage(width, height,
hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
@@ -973,41 +906,10 @@
}
@LayoutlibDelegate
- /*package*/ static void native_drawText(long nativeCanvas,
- final char[] text, final int index, final int count,
- final float startX, final float startY, final int flags, long paint,
- final long typeface) {
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
- // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
- // Any change to this method should be reflected in Paint.measureText
-
- // assert that the typeface passed is actually the one stored in paint.
- assert (typeface == paintDelegate.mNativeTypeface);
-
-
- // Paint.TextAlign indicates how the text is positioned relative to X.
- // LEFT is the default and there's nothing to do.
- float x = startX;
- int limit = index + count;
- boolean isRtl = flags == Canvas.DIRECTION_RTL;
- if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
- RectF bounds = paintDelegate.measureText(text, index, count, isRtl);
- float m = bounds.right - bounds.left;
- if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
- x -= m / 2;
- } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
- x -= m;
- }
- }
-
- new BidiRenderer(graphics, paintDelegate, text).renderText(
- index, limit, isRtl, null, 0, true, x, startY);
- }
- });
+ /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count,
+ float startX, float startY, int flags, long paint, long typeface) {
+ drawText(nativeCanvas, text, index, count, startX, startY, flags == Canvas.DIRECTION_RTL,
+ paint, typeface);
}
@LayoutlibDelegate
@@ -1024,38 +926,19 @@
@LayoutlibDelegate
/*package*/ static void native_drawTextRun(long nativeCanvas, String text,
int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, long paint, long typeface) {
+ float x, float y, boolean isRtl, long paint, long typeface) {
int count = end - start;
char[] buffer = TemporaryBuffer.obtain(count);
TextUtils.getChars(text, start, end, buffer, 0);
- native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
+ drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
}
@LayoutlibDelegate
/*package*/ static void native_drawTextRun(long nativeCanvas, char[] text,
int start, int count, int contextStart, int contextCount,
- float x, float y, int flags, long paint, long typeface) {
- native_drawText(nativeCanvas, text, start, count, x, y, flags, paint, typeface);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawPosText(long nativeCanvas,
- char[] text, int index,
- int count, float[] pos,
- long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPosText is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawPosText(long nativeCanvas,
- String text, float[] pos,
- long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPosText is not supported.", null, null /*data*/);
+ float x, float y, boolean isRtl, long paint, long typeface) {
+ drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
}
@LayoutlibDelegate
@@ -1064,7 +947,7 @@
int count, long path,
float hOffset,
float vOffset, int bidiFlags,
- long paint) {
+ long paint, long typeface) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -1075,7 +958,8 @@
String text, long path,
float hOffset,
float vOffset,
- int flags, long paint) {
+ int bidiFlags, long paint,
+ long typeface) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -1095,6 +979,7 @@
sManager.removeJavaReferenceFor(nativeCanvas);
}
+
// ---- Private delegate/helper methods ----
/**
@@ -1132,6 +1017,41 @@
canvasDelegate.mSnapshot.draw(drawable);
}
+ private static void drawText(long nativeCanvas, final char[] text, final int index,
+ final int count, final float startX, final float startY, final boolean isRtl,
+ long paint, final long typeface) {
+
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ @Override
+ public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
+ // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+ // Any change to this method should be reflected in Paint.measureText
+
+ // assert that the typeface passed is actually the one stored in paint.
+ assert (typeface == paintDelegate.mNativeTypeface);
+
+ // Paint.TextAlign indicates how the text is positioned relative to X.
+ // LEFT is the default and there's nothing to do.
+ float x = startX;
+ int limit = index + count;
+ if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+ RectF bounds = paintDelegate.measureText(text, index, count, null, 0,
+ isRtl);
+ float m = bounds.right - bounds.left;
+ if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+ x -= m / 2;
+ } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+ x -= m;
+ }
+ }
+
+ new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x, startY)
+ .renderText(index, limit, isRtl, null, 0, true);
+ }
+ });
+ }
+
private Canvas_Delegate(Bitmap_Delegate bitmap) {
mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 5adf4ca..24ef189 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -602,7 +602,7 @@
return 0;
}
- RectF bounds = delegate.measureText(text, index, count, isRtl(bidiFlags));
+ RectF bounds = delegate.measureText(text, index, count, null, 0, bidiFlags);
return bounds.right - bounds.left;
}
@@ -618,11 +618,11 @@
}
@LayoutlibDelegate
- /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
- float maxWidth, int bidiFlags, float[] measuredWidth) {
+ /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, char[] text,
+ int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
return 0;
}
@@ -641,7 +641,7 @@
}
// measure from start to end
- RectF bounds = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags));
+ RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
float res = bounds.right - bounds.left;
if (measuredWidth != null) {
@@ -660,10 +660,11 @@
}
@LayoutlibDelegate
- /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
+ /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, String text,
+ boolean measureForwards,
float maxWidth, int bidiFlags, float[] measuredWidth) {
- return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
- bidiFlags, measuredWidth);
+ return native_breakText(nativePaint, nativeTypeface, text.toCharArray(), 0, text.length(),
+ maxWidth, bidiFlags, measuredWidth);
}
@LayoutlibDelegate
@@ -950,8 +951,25 @@
@LayoutlibDelegate
/*package*/ static int native_getTextWidths(long native_object, long native_typeface,
char[] text, int index, int count, int bidiFlags, float[] widths) {
- return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
- index, count, bidiFlags, widths, 0);
+
+ if (widths != null) {
+ for (int i = 0; i< count; i++) {
+ widths[i]=0;
+ }
+ }
+ // get the delegate from the native int.
+ Paint_Delegate delegate = sManager.getDelegate(native_object);
+ if (delegate == null) {
+ return 0;
+ }
+
+ // native_typeface is passed here since Framework's old implementation did not have the
+ // typeface object associated with the Paint. Since, we follow the new framework way,
+ // we store the typeface with the paint and use it directly.
+ assert (native_typeface == delegate.mNativeTypeface);
+
+ RectF bounds = delegate.measureText(text, index, count, widths, 0, bidiFlags);
+ return ((int) (bounds.right - bounds.left));
}
@LayoutlibDelegate
@@ -971,7 +989,7 @@
@LayoutlibDelegate
/*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
char[] text, int index, int count, int contextIndex, int contextCount,
- int flags, float[] advances, int advancesIndex) {
+ boolean isRtl, float[] advances, int advancesIndex) {
if (advances != null)
for (int i = advancesIndex; i< advancesIndex+count; i++)
@@ -987,25 +1005,21 @@
// we store the typeface with the paint and use it directly.
assert (native_typeface == delegate.mNativeTypeface);
- boolean isRtl = isRtl(flags);
-
- int limit = index + count;
- RectF bounds = new BidiRenderer(null, delegate, text).renderText(
- index, limit, isRtl, advances, advancesIndex, false, 0, 0);
+ RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, isRtl);
return bounds.right - bounds.left;
}
@LayoutlibDelegate
/*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
String text, int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex) {
+ boolean isRtl, float[] advances, int advancesIndex) {
// FIXME: support contextStart and contextEnd
int count = end - start;
char[] buffer = TemporaryBuffer.obtain(count);
TextUtils.getChars(text, start, end, buffer, 0);
return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
- contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
+ contextStart, contextEnd - contextStart, isRtl, advances, advancesIndex);
}
@LayoutlibDelegate
@@ -1062,7 +1076,7 @@
// assert that the typeface passed is actually the one that we had stored.
assert (native_typeface == delegate.mNativeTypeface);
- delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
+ delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
}
@LayoutlibDelegate
@@ -1158,9 +1172,16 @@
}
}
- /*package*/ RectF measureText(char[] text, int index, int count, boolean isRtl) {
- return new BidiRenderer(null, this, text).renderText(
- index, index + count, isRtl, null, 0, false, 0, 0);
+ /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
+ int advancesIndex, int bidiFlags) {
+ return new BidiRenderer(null, this, text)
+ .renderText(index, index + count, bidiFlags, advances, advancesIndex, false);
+ }
+
+ /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
+ int advancesIndex, boolean isRtl) {
+ return new BidiRenderer(null, this, text)
+ .renderText(index, index + count, isRtl, advances, advancesIndex, false);
}
private float getFontMetrics(FontMetrics metrics) {
@@ -1198,15 +1219,4 @@
delegate.mFlags &= ~flagMask;
}
}
-
- private static boolean isRtl(int flag) {
- switch(flag) {
- case Paint.BIDI_RTL:
- case Paint.BIDI_FORCE_RTL:
- case Paint.BIDI_DEFAULT_RTL:
- return true;
- default:
- return false;
- }
- }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 0ec7115..7153a90 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -297,14 +297,15 @@
}
@LayoutlibDelegate
- /*package*/ static void native_arcTo(long nPath, RectF oval,
+ /*package*/ static void native_arcTo(long nPath, float left, float top, float right,
+ float bottom,
float startAngle, float sweepAngle, boolean forceMoveTo) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
}
- pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
+ pathDelegate.arcTo(left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}
@LayoutlibDelegate
@@ -353,8 +354,8 @@
}
@LayoutlibDelegate
- /*package*/ static void native_addArc(long nPath, RectF oval,
- float startAngle, float sweepAngle) {
+ /*package*/ static void native_addArc(long nPath, float left, float top, float right,
+ float bottom, float startAngle, float sweepAngle) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -362,13 +363,13 @@
// because x/y is the center of the circle, need to offset this by the radius
pathDelegate.mPath.append(new Arc2D.Float(
- oval.left, oval.top, oval.width(), oval.height(),
+ left, top, right - left, bottom - top,
-startAngle, -sweepAngle, Arc2D.OPEN), false);
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(
- long nPath, RectF rect, float rx, float ry, int dir) {
+ /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+ float bottom, float rx, float ry, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
@@ -376,14 +377,15 @@
}
pathDelegate.mPath.append(new RoundRectangle2D.Float(
- rect.left, rect.top, rect.width(), rect.height(), rx * 2, ry * 2), false);
+ left, top, right - left, bottom - top, rx * 2, ry * 2), false);
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(long nPath, RectF rect, float[] radii, int dir) {
+ /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+ float bottom, float[] radii, int dir) {
// Java2D doesn't support different rounded corners in each corner, so just use the
// first value.
- native_addRoundRect(nPath, rect, radii[0], radii[1], dir);
+ native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
// there can be a case where this API is used but with similar values for all corners, so
// in that case we don't warn.
@@ -710,14 +712,19 @@
* start of the arc. However, if the path is empty, then we call moveTo()
* with the first point of the arc. The sweep angle is tread mod 360.
*
- * @param oval The bounds of oval defining shape and size of the arc
+ * @param left The left of oval defining shape and size of the arc
+ * @param top The top of oval defining shape and size of the arc
+ * @param right The right of oval defining shape and size of the arc
+ * @param bottom The bottom of oval defining shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise, treated
* mod 360.
* @param forceMoveTo If true, always begin a new contour with the arc
*/
- private void arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) {
- Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), -startAngle,
+ private void arcTo(float left, float top, float right, float bottom, float startAngle,
+ float sweepAngle,
+ boolean forceMoveTo) {
+ Arc2D arc = new Arc2D.Float(left, top, right - left, bottom - top, -startAngle,
-sweepAngle, Arc2D.OPEN);
mPath.append(arc, true /*connect*/);
diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
index 973fa0e..6247dae 100644
--- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
@@ -37,14 +37,17 @@
switch (dir) {
case 0: // Layout.DIR_REQUEST_LTR
- case 1: // Layout.DIR_REQUEST_RTL
- break; // No change.
- case -1:
- dir = Bidi.LEVEL_DEFAULT_LTR;
+ dir = Bidi.LTR;
break;
- case -2:
+ case 1: // Layout.DIR_REQUEST_RTL
+ dir = Bidi.RTL;
+ break;
+ case -1: // Layout.DIR_REQUEST_DEFAULT_RTL
dir = Bidi.LEVEL_DEFAULT_RTL;
break;
+ case -2: // Layout.DIR_REQUEST_DEFAULT_LTR
+ dir = Bidi.LEVEL_DEFAULT_LTR;
+ break;
default:
// Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue.
Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index d32f6ee..997b199 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -47,8 +47,8 @@
}
@Override
- public void resized(Rect arg1, Rect arg1p5, Rect arg2, Rect arg3,
- boolean arg4, Configuration arg5) throws RemoteException {
+ public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, boolean b,
+ Configuration configuration) throws RemoteException {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index e1064b1..130500a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -85,10 +85,11 @@
// pass for now.
return false;
}
+
@Override
- public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
- int arg4_5, Rect arg5Z, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b,
- Surface arg8) throws RemoteException {
+ public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
+ int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
+ Configuration configuration, Surface surface) throws RemoteException {
// pass for now.
return 0;
}
diff --git a/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java b/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java
new file mode 100644
index 0000000..36efc3a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -0,0 +1,78 @@
+/*
+ * 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 dalvik.system;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide implementation of a select few native methods of {@link VMRuntime}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of VMRuntime have been replaced
+ * by calls to methods of the same name in this delegate class.
+ */
+public class VMRuntime_Delegate {
+
+ // Copied from libcore/libdvm/src/main/java/dalvik/system/VMRuntime
+ @LayoutlibDelegate
+ /*package*/ static Object newUnpaddedArray(VMRuntime runtime, Class<?> componentType,
+ int minLength) {
+ // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
+ // allocations are 8byte aligned so having 4bytes of array data avoids padding.
+ if (!componentType.isPrimitive()) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return java.lang.reflect.Array.newInstance(componentType, size);
+ } else if (componentType == char.class) {
+ int bytes = 20 + (2 * minLength);
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes / 2;
+ return new char[size];
+ } else if (componentType == int.class) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return new int[size];
+ } else if (componentType == byte.class) {
+ int bytes = 20 + minLength;
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes;
+ return new byte[size];
+ } else if (componentType == boolean.class) {
+ int bytes = 20 + minLength;
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes;
+ return new boolean[size];
+ } else if (componentType == short.class) {
+ int bytes = 20 + (2 * minLength);
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes / 2;
+ return new short[size];
+ } else if (componentType == float.class) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return new float[size];
+ } else if (componentType == long.class) {
+ return new long[minLength];
+ } else if (componentType == double.class) {
+ return new double[minLength];
+ } else {
+ assert componentType == void.class;
+ throw new IllegalArgumentException("Can't allocate an array of void");
+ }
+ }
+
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index 19d249b..71947b0 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -148,12 +148,12 @@
}
@LayoutlibDelegate
- /*package*/ static String getISO3CountryNative(String locale) {
+ /*package*/ static String getISO3Country(String locale) {
return "";
}
@LayoutlibDelegate
- /*package*/ static String getISO3LanguageNative(String locale) {
+ /*package*/ static String getISO3Language(String locale) {
return "";
}
@@ -184,11 +184,6 @@
}
@LayoutlibDelegate
- /*package*/ static String languageTagForLocale(String locale) {
- return "";
- }
-
- @LayoutlibDelegate
/*package*/ static boolean initLocaleDataNative(String locale, LocaleData result) {
// Used by Calendar.
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index 274516c..8b362ec 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -41,27 +41,29 @@
*/
public class TestDelegates extends TestCase {
+ private List<String> mErrors = new ArrayList<String>();
+
public void testNativeDelegates() {
final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
- final int count = classes.length;
- for (int i = 0 ; i < count ; i++) {
- loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+ mErrors.clear();
+ for (String clazz : classes) {
+ loadAndCompareClasses(clazz, clazz + "_Delegate");
}
+ assertTrue(getErrors(), mErrors.isEmpty());
}
public void testMethodDelegates() {
final String[] methods = CreateInfo.DELEGATE_METHODS;
- final int count = methods.length;
- for (int i = 0 ; i < count ; i++) {
- String methodName = methods[i];
-
+ mErrors.clear();
+ for (String methodName : methods) {
// extract the class name
String className = methodName.substring(0, methodName.indexOf('#'));
String targetClassName = className.replace('$', '_') + "_Delegate";
loadAndCompareClasses(className, targetClassName);
}
+ assertTrue(getErrors(), mErrors.isEmpty());
}
private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
@@ -73,9 +75,9 @@
compare(originalClass, delegateClass);
} catch (ClassNotFoundException e) {
- fail("Failed to load class: " + e.getMessage());
+ mErrors.add("Failed to load class: " + e.getMessage());
} catch (SecurityException e) {
- fail("Failed to load class: " + e.getMessage());
+ mErrors.add("Failed to load class: " + e.getMessage());
}
}
@@ -122,35 +124,39 @@
parameters);
// check the return type of the methods match.
- assertTrue(
- String.format("Delegate method %1$s.%2$s does not match the corresponding " +
- "framework method which returns %3$s",
- delegateClass.getName(),
- getMethodName(delegateMethod),
- originalMethod.getReturnType().getName()),
- delegateMethod.getReturnType() == originalMethod.getReturnType());
+ if (delegateMethod.getReturnType() != originalMethod.getReturnType()) {
+ mErrors.add(
+ String.format("Delegate method %1$s.%2$s does not match the " +
+ "corresponding framework method which returns %3$s",
+ delegateClass.getName(),
+ getMethodName(delegateMethod),
+ originalMethod.getReturnType().getName()));
+ }
// check that the method has the annotation
- assertNotNull(
- String.format(
- "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation",
- delegateMethod.getName(),
- originalClass.getName()),
- delegateMethod.getAnnotation(LayoutlibDelegate.class));
+ if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+ mErrors.add(
+ String.format("Delegate method %1$s for class %2$s does not have the " +
+ "@LayoutlibDelegate annotation",
+ delegateMethod.getName(),
+ originalClass.getName()));
+ }
// check that the method is static
- assertTrue(
- String.format(
- "Delegate method %1$s for class %2$s is not static",
- delegateMethod.getName(),
- originalClass.getName()),
- (delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
+ if ((delegateMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
+ mErrors.add(
+ String.format(
+ "Delegate method %1$s for class %2$s is not static",
+ delegateMethod.getName(),
+ originalClass.getName())
+ );
+ }
// add the method as checked.
checkedDelegateMethods.add(delegateMethod);
} catch (NoSuchMethodException e) {
String name = getMethodName(originalMethod, parameters);
- fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
+ mErrors.add(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
}
}
@@ -167,12 +173,12 @@
continue;
}
- assertTrue(
- String.format(
- "Delegate method %1$s.%2$s is not used anymore and must be removed",
- delegateClass.getName(),
- getMethodName(delegateMethod)),
- checkedDelegateMethods.contains(delegateMethod));
+ if (!checkedDelegateMethods.contains(delegateMethod)) {
+ mErrors.add(String.format(
+ "Delegate method %1$s.%2$s is not used anymore and must be removed",
+ delegateClass.getName(),
+ getMethodName(delegateMethod)));
+ }
}
}
@@ -203,4 +209,12 @@
return sb.toString();
}
+
+ private String getErrors() {
+ StringBuilder s = new StringBuilder();
+ for (String error : mErrors) {
+ s.append(error).append('\n');
+ }
+ return s.toString();
+ }
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 1f7a28e..552fb6c 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -152,6 +152,7 @@
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
"android.os.SystemProperties#native_get",
+ "dalvik.system.VMRuntime#newUnpaddedArray"
};
/**
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 59b48e4..31900ecd7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -49,6 +49,10 @@
/** {@hide} */
public static final String hiddenSSIDVarName = "scan_ssid";
/** {@hide} */
+ public static final String pmfVarName = "ieee80211w";
+ /** {@hide} */
+ public static final String updateIdentiferVarName = "update_identifier";
+ /** {@hide} */
public static final int INVALID_NETWORK_ID = -1;
/**
* Recognized key management schemes.
@@ -264,6 +268,18 @@
public boolean hiddenSSID;
/**
+ * This is a network that requries Protected Management Frames (PMF).
+ * @hide
+ */
+ public boolean requirePMF;
+
+ /**
+ * Update identifier, for Passpoint network.
+ * @hide
+ */
+ public String updateIdentifier;
+
+ /**
* The set of key management protocols supported by this configuration.
* See {@link KeyMgmt} for descriptions of the values.
* Defaults to WPA-PSK WPA-EAP.
@@ -1155,6 +1171,8 @@
dest.writeInt(wepTxKeyIndex);
dest.writeInt(priority);
dest.writeInt(hiddenSSID ? 1 : 0);
+ dest.writeInt(requirePMF ? 1 : 0);
+ dest.writeString(updateIdentifier);
writeBitSet(dest, allowedKeyManagement);
writeBitSet(dest, allowedProtocols);
@@ -1196,6 +1214,9 @@
config.wepTxKeyIndex = in.readInt();
config.priority = in.readInt();
config.hiddenSSID = in.readInt() != 0;
+ config.requirePMF = in.readInt() != 0;
+ config.updateIdentifier = in.readString();
+
config.allowedKeyManagement = readBitSet(in);
config.allowedProtocols = readBitSet(in);
config.allowedAuthAlgorithms = readBitSet(in);
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index e7e6767..ddca85e 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -221,12 +221,22 @@
return key;
}
- private Object getListener(int key, boolean force) {
- Log.d(TAG, "getListener() key=" + key + " force=" + force);
+ private Object peekListener(int key) {
+ Log.d(TAG, "peekListener() key=" + key);
if (key == INVALID_LISTENER_KEY)
return null;
synchronized (mListenerMapLock) {
- if (!force) {
+ return mListenerMap.get(key);
+ }
+ }
+
+
+ private Object getListener(int key, boolean forceRemove) {
+ Log.d(TAG, "getListener() key=" + key + " force=" + forceRemove);
+ if (key == INVALID_LISTENER_KEY)
+ return null;
+ synchronized (mListenerMapLock) {
+ if (!forceRemove) {
int count = mListenerMapCount.get(key);
Log.d(TAG, "count=" + count);
mListenerMapCount.put(key, --count);
@@ -322,7 +332,7 @@
break;
case START_OSU_BROWSER:
- listener = getListener(message.arg2, true);
+ listener = peekListener(message.arg2);
if (listener != null) {
ParcelableString str = (ParcelableString) message.obj;
if (str == null || str.string == null)