From 1f415fa2eaa3819e99021dd54ed62aabc6689ea0 Mon Sep 17 00:00:00 2001
From: Robert Ly
Date: Tue, 28 Feb 2012 13:05:50 -0800
Subject: [PATCH 01/12] docs: nfc ndef helper methods bug 5957772
Change-Id: I9c8356f44c1f9cc4a5187d40816cbfdd6212ab6e
---
.../html/guide/topics/connectivity/nfc/nfc.jd | 103 +++++++++++-------
1 file changed, 63 insertions(+), 40 deletions(-)
diff --git a/docs/html/guide/topics/connectivity/nfc/nfc.jd b/docs/html/guide/topics/connectivity/nfc/nfc.jd
index 51c7bee04f26d..5011872760a96 100644
--- a/docs/html/guide/topics/connectivity/nfc/nfc.jd
+++ b/docs/html/guide/topics/connectivity/nfc/nfc.jd
@@ -318,8 +318,8 @@ other two intents, giving the user a better experience.
- The uses-feature element so that your application shows up in Google Play
+ only for devices that have NFC hardware:
<uses-feature android:name="android.hardware.nfc" android:required="true" />
@@ -511,13 +511,24 @@ contain the payload and allow you to enumerate the tag's technologies:
Creating Common Types of NDEF Records
This section describes how to create common types of NDEF records to help you when writing to
-NFC tags or sending data with Android Beam. It also describes how to create the corresponding
+NFC tags or sending data with Android Beam. Starting with Android 4.0 (API level 14), the
+{@link android.nfc.NdefRecord#createUri createUri()} method is available to help you create
+URI records automatically. Starting in Android 4.1 (API level 16), {@link android.nfc.NdefRecord#createExternal createExternal()}
+and {@link android.nfc.NdefRecord#createMime createMime()} are available to help you create
+MIME and external type NDEF records. Use these helper methods whenever possible to avoid mistakes
+when manually creating NDEF records.
+
+
+This section also describes how to create the corresponding
intent filter for the record. All of these NDEF record examples should be in the first NDEF
record of the NDEF message that you are writing to a tag or beaming.
TNF_ABSOLUTE_URI
-Given the following {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record, which is
-stored as the first record inside of an {@link android.nfc.NdefMessage}:
+Note: We recommend that you use the
+ RTD_URI type instead
+ of {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI}, because it is more efficient.
+
+You can create a {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record in the following way:
NdefRecord uriRecord = new NdefRecord(
@@ -526,7 +537,7 @@ NdefRecord uriRecord = new NdefRecord(
new byte[0], new byte[0]);
-the intent filter would look like this:
+The intent filter for the previous NDEF record would look like this:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
@@ -537,32 +548,35 @@ NdefRecord uriRecord = new NdefRecord(
</intent-filter>
-
TNF_MIME_MEDIA
-Given the following {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record, which is stored as
-the first record inside
-of an {@link android.nfc.NdefMessage}:
+You can create a {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record in the following ways.
+
+Using the {@link android.nfc.NdefRecord#createMime createMime()} method:
+
+NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
+ "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
+
+
+Creating the {@link android.nfc.NdefRecord} manually:
NdefRecord mimeRecord = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA ,
- "application/com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
+ "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
-the intent filter would look like this:
+The intent filter for the previous NDEF records would look like this:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="application/com.example.android.beam" />
+ <data android:mimeType="application/vnd.com.example.android.beam" />
</intent-filter>
-
TNF_WELL_KNOWN with RTD_TEXT
-Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as
-the first record inside of an {@link android.nfc.NdefMessage}:
+You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following way:
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
@@ -592,9 +606,20 @@ public NdefRecord createTextRecord(String payload, Locale locale, boolean encode
TNF_WELL_KNOWN with RTD_URI
-Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as
-the first record inside of an {@link android.nfc.NdefMessage}:
+You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following ways.
+Using the {@link android.nfc.NdefRecord#createUri(String)} method:
+
+NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
+
+
+Using the {@link android.nfc.NdefRecord#createUri(Uri)} method:
+
+Uri uri = new Uri("http://example.com");
+NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
+
+
+Creating the {@link android.nfc.NdefRecord} manually:
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix
@@ -604,7 +629,7 @@ NdefRecord rtdUriRecord = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
-the intent filter would look like this:
+The intent filter for the previous NDEF records would look like this:
<intent-filter>
@@ -617,24 +642,32 @@ NdefRecord rtdUriRecord = new NdefRecord(
TNF_EXTERNAL_TYPE
-Given the following {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record, which is stored
-as the first record inside of an {@link android.nfc.NdefMessage}:
+You can create a {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record in the following ways:
+Using the {@link android.nfc.NdefRecord#createExternal createExternal()} method:
+
+byte[] payload; //assign to your data
+String domain = "com.example"; //usually your app's package name
+String type = "externalType";
+NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
+
+
+Creating the {@link android.nfc.NdefRecord} manually:
byte[] payload;
...
-NdefRecord mimeRecord = new NdefRecord(
- NdefRecord.TNF_EXTERNAL_TYPE, "example.com:externalType", new byte[0], payload);
+NdefRecord extRecord = new NdefRecord(
+ NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
-the intent filter would look like this:
+The intent filter for the previous NDEF records would look like this:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="vnd.android.nfc"
android:host="ext"
- android:pathPrefix="/example.com:externalType"/>
+ android:pathPrefix="/com.example:externalType"/>
</intent-filter>
@@ -840,8 +873,8 @@ public class Beam extends Activity implements CreateNdefMessageCallback {
String text = ("Beam me up, Android!\n\n" +
"Beam Time: " + System.currentTimeMillis());
NdefMessage msg = new NdefMessage(
- new NdefRecord[] { createMimeRecord(
- "application/com.example.android.beam", text.getBytes())
+ new NdefRecord[] { createMime(
+ "application/vnd.com.example.android.beam", text.getBytes())
/**
* The Android Application Record (AAR) is commented out. When a device
* receives a push with an AAR in it, the application specified in the AAR
@@ -882,22 +915,12 @@ public class Beam extends Activity implements CreateNdefMessageCallback {
// record 0 contains the MIME type, record 1 is the AAR, if present
textView.setText(new String(msg.getRecords()[0].getPayload()));
}
-
- /**
- * Creates a custom MIME type encapsulated in an NDEF record
- */
- public NdefRecord createMimeRecord(String mimeType, byte[] payload) {
- byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
- NdefRecord mimeRecord = new NdefRecord(
- NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);
- return mimeRecord;
- }
}
Note that this code comments out an AAR, which you can remove. If you enable the AAR, the
application specified in the AAR always receives the Android Beam message. If the application is not
-present, Google Play launches to download the application. Therefore, the following intent
+present, Google Play is started to download the application. Therefore, the following intent
filter is not technically necessary for Android 4.0 devices or later if the AAR is used:
@@ -905,13 +928,13 @@ filter is not technically necessary for Android 4.0 devices or later if the AAR
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="application/com.example.android.beam"/>
+ <data android:mimeType="application/vnd.com.example.android.beam"/>
</intent-filter>
With this intent filter, the com.example.android.beam application now can be started
when it scans an NFC tag or receives an Android Beam with an AAR of
type com.example.android.beam, or when an NDEF formatted message contains a MIME record
-of type application/com.example.android.beam.
+of type application/vnd.com.example.android.beam.
Even though AARs guarantee an application is started or downloaded, intent filters are
recommended, because they let you start an Activity of your choice in your
From dd45d01128423a82652a3c9d77fa393631d95229 Mon Sep 17 00:00:00 2001
From: Eric Laurent
Date: Mon, 8 Oct 2012 09:04:34 -0700
Subject: [PATCH 02/12] enforce camera sound according to country code
Use mcc config overlay mechanism to enforce camera shutter sounds
in countries where it is mandatory.
Property ro.camera.sound.forced is not needed anymore.
When camera sound is forced, STREAM_SYSTEM_ENFORCED is removed from
streams affected by ringer mode and its volume is
maxed out. AudioSystem.FORCE_SYSTEM_ENFORCED is sent to audio
policy manager to alter the routing policy for STREAM_SYSTEM_ENFORCED.
Also fix streams being unmuted when settings are reloaded
upon user switch while in silent mode.
Add ringer mode to audio service dump.
Bug 7032634.
Change-Id: Iceea5bba3b8d3aabf8e42b222deb33a893dc8f38
---
core/res/res/values-mcc440/config.xml | 25 +++
core/res/res/values/config.xml | 3 +
core/res/res/values/symbols.xml | 1 +
media/java/android/media/AudioService.java | 210 ++++++++++++++++----
media/java/android/media/AudioSystem.java | 6 +-
media/java/android/media/IAudioService.aidl | 2 +
6 files changed, 210 insertions(+), 37 deletions(-)
create mode 100644 core/res/res/values-mcc440/config.xml
diff --git a/core/res/res/values-mcc440/config.xml b/core/res/res/values-mcc440/config.xml
new file mode 100644
index 0000000000000..4ca1677fdd8da
--- /dev/null
+++ b/core/res/res/values-mcc440/config.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ true
+
+
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16960c8737039..a00f071fd548c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -981,4 +981,7 @@
-->
false
+
+ false
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 281d92ade7357..e615fd40ae4d1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
+
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 7d173913a308e..f26d322afea98 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -462,7 +462,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mVolumePanel = new VolumePanel(context, this);
mMode = AudioSystem.MODE_NORMAL;
mForcedUseForComm = AudioSystem.FORCE_NONE;
+
createAudioSystemThread();
+
+ boolean cameraSoundForced = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_camera_sound_forced);
+ mCameraSoundForced = new Boolean(cameraSoundForced);
+ sendMsg(mAudioHandler,
+ MSG_SET_FORCE_USE,
+ SENDMSG_QUEUE,
+ AudioSystem.FOR_SYSTEM,
+ cameraSoundForced ?
+ AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
+ null,
+ 0);
+
readPersistedSettings();
mSettingsObserver = new SettingsObserver();
updateStreamVolumeAlias(false /*updateVolumes*/);
@@ -585,6 +599,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mStreamStates[i].dump(pw);
pw.println("");
}
+ pw.print("\n- mute affected streams = 0x");
+ pw.println(Integer.toHexString(mMuteAffectedStreams));
}
@@ -634,35 +650,44 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
synchronized(mSettingsLock) {
mRingerMode = ringerMode;
- }
- // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
- // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated.
- mVibrateSetting = getValueForVibrateSetting(0,
- AudioManager.VIBRATE_TYPE_NOTIFICATION,
- mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
- : AudioManager.VIBRATE_SETTING_OFF);
- mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
- AudioManager.VIBRATE_TYPE_RINGER,
- mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
- : AudioManager.VIBRATE_SETTING_OFF);
+ // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
+ // are still needed while setVibrateSetting() and getVibrateSetting() are being
+ // deprecated.
+ mVibrateSetting = getValueForVibrateSetting(0,
+ AudioManager.VIBRATE_TYPE_NOTIFICATION,
+ mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
+ : AudioManager.VIBRATE_SETTING_OFF);
+ mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
+ AudioManager.VIBRATE_TYPE_RINGER,
+ mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
+ : AudioManager.VIBRATE_SETTING_OFF);
- // make sure settings for ringer mode are consistent with device type: non voice capable
- // devices (tablets) include media stream in silent mode whereas phones don't.
- mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
- (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
- UserHandle.USER_CURRENT);
- if (mVoiceCapable) {
- mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
- } else {
- mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+ // make sure settings for ringer mode are consistent with device type: non voice capable
+ // devices (tablets) include media stream in silent mode whereas phones don't.
+ mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
+ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
+ UserHandle.USER_CURRENT);
+ if (mVoiceCapable) {
+ mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
+ } else {
+ mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+ }
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ }
+
+ Settings.System.putIntForUser(cr,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ mRingerModeAffectedStreams,
+ UserHandle.USER_CURRENT);
}
- Settings.System.putIntForUser(cr,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- mRingerModeAffectedStreams,
- UserHandle.USER_CURRENT);
mMuteAffectedStreams = System.getIntForUser(cr,
System.MUTE_STREAMS_AFFECTED,
@@ -2601,12 +2626,18 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// only be stale values
// on first call to readSettings() at init time, muteCount() is always 0 so we will
// always create entries for default device
- if ((muteCount() == 0) && (mStreamType == AudioSystem.STREAM_SYSTEM) ||
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
(mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
- mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT,
- 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
- mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT,
- 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
+ int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ index = mIndexMax;
+ }
+ }
+ if (muteCount() == 0) {
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+ }
+ mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
return;
}
@@ -2618,10 +2649,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
remainingDevices &= ~device;
// ignore settings for fixed volume devices: volume should always be at max
- if ((muteCount() == 0) &&
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
+ if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
- mIndex.put(device, mIndexMax);
+ if (muteCount() == 0) {
+ mIndex.put(device, mIndexMax);
+ }
mLastAudibleIndex.put(device, mIndexMax);
continue;
}
@@ -2676,7 +2708,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
this,
PERSIST_DELAY);
}
- mIndex.put(device, getValidIndex(10 * index));
+ if (muteCount() == 0) {
+ mIndex.put(device, getValidIndex(10 * index));
+ }
}
}
@@ -2716,6 +2750,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
int oldIndex = getIndex(device, false /* lastAudible */);
index = getValidIndex(index);
+ synchronized (mCameraSoundForced) {
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
+ index = mIndexMax;
+ }
+ }
mIndex.put(device, getValidIndex(index));
if (oldIndex != index) {
@@ -2819,6 +2858,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ public synchronized void setAllIndexesToMax() {
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(mIndexMax);
+ }
+ set = mLastAudibleIndex.entrySet();
+ i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(mIndexMax);
+ }
+ }
+
public synchronized void mute(IBinder cb, boolean state) {
VolumeDeathHandler handler = getDeathHandler(cb, state);
if (handler == null) {
@@ -2967,6 +3021,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
private void dump(PrintWriter pw) {
+ pw.print(" Mute count: ");
+ pw.println(muteCount());
pw.print(" Current: ");
Set set = mIndex.entrySet();
Iterator i = set.iterator();
@@ -3215,6 +3271,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// Restore forced usage for communcations and record
AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
+ AudioSystem.setForceUse(AudioSystem.FOR_SYSTEM, mCameraSoundForced ?
+ AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE);
// Restore stream volumes
int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -3372,6 +3430,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
} else {
ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
}
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ }
if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
/*
* Ensure all stream types that should be affected by ringer mode
@@ -5587,6 +5652,48 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
0,
null,
0);
+
+ boolean cameraSoundForced = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_camera_sound_forced);
+ synchronized (mSettingsLock) {
+ synchronized (mCameraSoundForced) {
+ if (cameraSoundForced != mCameraSoundForced) {
+ mCameraSoundForced = cameraSoundForced;
+
+ VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+ if (cameraSoundForced) {
+ s.setAllIndexesToMax();
+ mRingerModeAffectedStreams &=
+ ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
+ false /*lastAudible*/);
+ s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
+ true /*lastAudible*/);
+ mRingerModeAffectedStreams |=
+ (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ // take new state into account for streams muted by ringer mode
+ setRingerModeInt(getRingerMode(), false);
+
+ sendMsg(mAudioHandler,
+ MSG_SET_FORCE_USE,
+ SENDMSG_QUEUE,
+ AudioSystem.FOR_SYSTEM,
+ cameraSoundForced ?
+ AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
+ null,
+ 0);
+
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0);
+ }
+ }
+ }
} catch (Exception e) {
Log.e(TAG, "Error retrieving device orientation: " + e);
}
@@ -5762,6 +5869,38 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
+ //==========================================================================================
+ // Camera shutter sound policy.
+ // config_camera_sound_forced configuration option in config.xml defines if the camera shutter
+ // sound is forced (sound even if the device is in silent mode) or not. This option is false by
+ // default and can be overridden by country specific overlay in values-mccXXX/config.xml.
+ //==========================================================================================
+
+ // cached value of com.android.internal.R.bool.config_camera_sound_forced
+ private Boolean mCameraSoundForced;
+
+ // called by android.hardware.Camera to populate CameraInfo.canDisableShutterSound
+ public boolean isCameraSoundForced() {
+ synchronized (mCameraSoundForced) {
+ return mCameraSoundForced;
+ }
+ }
+
+ private static final String[] RINGER_MODE_NAMES = new String[] {
+ "SILENT",
+ "VIBRATE",
+ "NORMAL"
+ };
+
+ private void dumpRingerMode(PrintWriter pw) {
+ pw.println("\nRinger mode: ");
+ pw.println("- mode: "+RINGER_MODE_NAMES[mRingerMode]);
+ pw.print("- ringer mode affected streams = 0x");
+ pw.println(Integer.toHexString(mRingerModeAffectedStreams));
+ pw.print("- ringer mode muted streams = 0x");
+ pw.println(Integer.toHexString(mRingerModeMutedStreams));
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
@@ -5770,6 +5909,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
dumpRCStack(pw);
dumpRCCStack(pw);
dumpStreamStates(pw);
+ dumpRingerMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 2cff4ffb64bc7..103e817b3ac52 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -354,7 +354,8 @@ public class AudioSystem
public static final int FORCE_DIGITAL_DOCK = 9;
public static final int FORCE_NO_BT_A2DP = 10;
public static final int FORCE_REMOTE_SUBMIX = 11;
- private static final int NUM_FORCE_CONFIG = 12;
+ public static final int FORCE_SYSTEM_ENFORCED = 12;
+ private static final int NUM_FORCE_CONFIG = 13;
public static final int FORCE_DEFAULT = FORCE_NONE;
// usage for setForceUse, must match AudioSystem::force_use
@@ -362,7 +363,8 @@ public class AudioSystem
public static final int FOR_MEDIA = 1;
public static final int FOR_RECORD = 2;
public static final int FOR_DOCK = 3;
- private static final int NUM_FORCE_USE = 4;
+ public static final int FOR_SYSTEM = 4;
+ private static final int NUM_FORCE_USE = 5;
// usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
public static final int SYNC_EVENT_NONE = 0;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7ae61cd3e924b..ea99069b0606c 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -153,4 +153,6 @@ interface IAudioService {
int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state);
AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
+
+ boolean isCameraSoundForced();
}
From 531c237b82a948ac6599ba472bb95e26f71006cf Mon Sep 17 00:00:00 2001
From: Amith Yamasani
Date: Mon, 8 Oct 2012 14:43:20 -0700
Subject: [PATCH 03/12] Add an upgrade step for settings moved to global.
For some reason, the original step didn't work for some testers. This re-applies the move, which
should be no-ops if the entries are already in the right table.
Bug: 7254629
Also moved a few more entries to the global initialization section. Otherwise they would write
into the wrong table.
Change-Id: Ic0f5c4e09680f5687d08dccf78063508b9c0584c
---
.../providers/settings/DatabaseHelper.java | 55 +++++++++++++------
1 file changed, 37 insertions(+), 18 deletions(-)
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0b61abe07038e..06892687ed9f5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -68,7 +68,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 93;
+ private static final int DATABASE_VERSION = 94;
private Context mContext;
private int mUserHandle;
@@ -1438,7 +1438,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
db.beginTransaction();
try {
// Move ringer mode from system to global settings
- String[] settingsToMove = { Settings.System.MODE_RINGER };
+ String[] settingsToMove = { Settings.Global.MODE_RINGER };
moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove, true);
db.setTransactionSuccessful();
@@ -1473,6 +1473,27 @@ public class DatabaseHelper extends SQLiteOpenHelper {
upgradeVersion = 93;
}
+ if (upgradeVersion == 93) {
+ // Redo this step, since somehow it didn't work the first time for some users
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ // Migrate now-global settings
+ String[] settingsToMove = hashsetToStringArray(SettingsProvider.sSystemGlobalKeys);
+ moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove, true);
+ settingsToMove = hashsetToStringArray(SettingsProvider.sSecureGlobalKeys);
+ moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ }
+ upgradeVersion = 94;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -1921,10 +1942,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
private void loadUISoundEffectsSettings(SQLiteStatement stmt) {
- loadIntegerSetting(stmt, Settings.System.POWER_SOUNDS_ENABLED,
- R.integer.def_power_sounds_enabled);
- loadStringSetting(stmt, Settings.System.LOW_BATTERY_SOUND,
- R.string.def_low_battery_sound);
loadBooleanSetting(stmt, Settings.System.DTMF_TONE_WHEN_DIALING,
R.bool.def_dtmf_tones_enabled);
loadBooleanSetting(stmt, Settings.System.SOUND_EFFECTS_ENABLED,
@@ -1932,17 +1949,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadBooleanSetting(stmt, Settings.System.HAPTIC_FEEDBACK_ENABLED,
R.bool.def_haptic_feedback);
- loadIntegerSetting(stmt, Settings.System.DOCK_SOUNDS_ENABLED,
- R.integer.def_dock_sounds_enabled);
- loadStringSetting(stmt, Settings.System.DESK_DOCK_SOUND,
- R.string.def_desk_dock_sound);
- loadStringSetting(stmt, Settings.System.DESK_UNDOCK_SOUND,
- R.string.def_desk_undock_sound);
- loadStringSetting(stmt, Settings.System.CAR_DOCK_SOUND,
- R.string.def_car_dock_sound);
- loadStringSetting(stmt, Settings.System.CAR_UNDOCK_SOUND,
- R.string.def_car_undock_sound);
-
loadIntegerSetting(stmt, Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
R.integer.def_lockscreen_sounds_enabled);
}
@@ -2158,9 +2164,22 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadStringSetting(stmt, Settings.Global.LOCK_SOUND,
R.string.def_lock_sound);
-
loadStringSetting(stmt, Settings.Global.UNLOCK_SOUND,
R.string.def_unlock_sound);
+ loadIntegerSetting(stmt, Settings.Global.POWER_SOUNDS_ENABLED,
+ R.integer.def_power_sounds_enabled);
+ loadStringSetting(stmt, Settings.Global.LOW_BATTERY_SOUND,
+ R.string.def_low_battery_sound);
+ loadIntegerSetting(stmt, Settings.Global.DOCK_SOUNDS_ENABLED,
+ R.integer.def_dock_sounds_enabled);
+ loadStringSetting(stmt, Settings.Global.DESK_DOCK_SOUND,
+ R.string.def_desk_dock_sound);
+ loadStringSetting(stmt, Settings.Global.DESK_UNDOCK_SOUND,
+ R.string.def_desk_undock_sound);
+ loadStringSetting(stmt, Settings.Global.CAR_DOCK_SOUND,
+ R.string.def_car_dock_sound);
+ loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND,
+ R.string.def_car_undock_sound);
loadSetting(stmt, Settings.Global.SET_INSTALL_LOCATION, 0);
loadSetting(stmt, Settings.Global.DEFAULT_INSTALL_LOCATION,
From b711d57ca4e2c6a1befbfa1a41f4b8094755a93f Mon Sep 17 00:00:00 2001
From: Victoria Lease
Date: Tue, 2 Oct 2012 13:14:11 -0700
Subject: [PATCH 04/12] Multiuser love for LocationManager
LocationManagerService now keeps track of the current user ID and
denies location requests made by all but the foreground user.
Additionally, location settings are now user-specific, rather than
global to the device. Location provider services now run as specific
users, and when the device's foreground user changes, we rebind to
appropriately-owned providers.
Bug: 6926385
Bug: 7247203
Change-Id: I346074959e96e52bcc77eeb188dffe322b690879
---
core/java/android/provider/Settings.java | 31 ++-
.../server/LocationManagerService.java | 184 +++++++++++-------
.../com/android/server/ServiceWatcher.java | 85 ++++----
.../server/location/GeocoderProxy.java | 9 +-
.../server/location/GpsLocationProvider.java | 5 +
.../location/LocationProviderInterface.java | 2 +
.../location/LocationProviderProxy.java | 14 +-
.../android/server/location/MockProvider.java | 5 +
.../server/location/PassiveProvider.java | 5 +
9 files changed, 222 insertions(+), 118 deletions(-)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3bbdf36550015..91c6db50ca113 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4052,7 +4052,20 @@ public final class Settings {
* @return true if the provider is enabled
*/
public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
- String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED);
+ return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId());
+ }
+
+ /**
+ * Helper method for determining if a location provider is enabled.
+ * @param cr the content resolver to use
+ * @param provider the location provider to query
+ * @param userId the userId to query
+ * @return true if the provider is enabled
+ * @hide
+ */
+ public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
+ String allowedProviders = Settings.Secure.getStringForUser(cr,
+ LOCATION_PROVIDERS_ALLOWED, userId);
return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
}
@@ -4064,6 +4077,19 @@ public final class Settings {
*/
public static final void setLocationProviderEnabled(ContentResolver cr,
String provider, boolean enabled) {
+ setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId());
+ }
+
+ /**
+ * Thread-safe method for enabling or disabling a single location provider.
+ * @param cr the content resolver to use
+ * @param provider the location provider to enable or disable
+ * @param enabled true if the provider should be enabled
+ * @param userId the userId for which to enable/disable providers
+ * @hide
+ */
+ public static final void setLocationProviderEnabledForUser(ContentResolver cr,
+ String provider, boolean enabled, int userId) {
// to ensure thread safety, we write the provider name with a '+' or '-'
// and let the SettingsProvider handle it rather than reading and modifying
// the list of enabled providers.
@@ -4072,7 +4098,8 @@ public final class Settings {
} else {
provider = "-" + provider;
}
- putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);
+ putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider,
+ userId);
}
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 0087b57456d2c..c85541881253f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -226,7 +226,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
updateProvidersLocked();
}
}
- });
+ }, UserHandle.USER_ALL);
mPackageMonitor.register(mContext, Looper.myLooper(), true);
// listen for user change
@@ -289,7 +289,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mContext,
LocationManager.NETWORK_PROVIDER,
NETWORK_LOCATION_SERVICE_ACTION,
- providerPackageNames, mLocationHandler);
+ providerPackageNames, mLocationHandler, mCurrentUserId);
if (networkProvider != null) {
mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
mProxyProviders.add(networkProvider);
@@ -303,7 +303,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mContext,
LocationManager.FUSED_PROVIDER,
FUSED_LOCATION_SERVICE_ACTION,
- providerPackageNames, mLocationHandler);
+ providerPackageNames, mLocationHandler, mCurrentUserId);
if (fusedLocationProvider != null) {
addProviderLocked(fusedLocationProvider);
mProxyProviders.add(fusedLocationProvider);
@@ -314,7 +314,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// bind to geocoder provider
- mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+ mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames,
+ mCurrentUserId);
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
@@ -326,11 +327,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
private void switchUser(int userId) {
mBlacklist.switchUser(userId);
- //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this
synchronized (mLock) {
- // TODO: inform previous user's Receivers that they will no longer receive updates
+ mLastLocation.clear();
+ for (LocationProviderInterface p : mProviders) {
+ updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
+ p.switchUser(userId);
+ }
mCurrentUserId = userId;
- // TODO: inform new user's Receivers that they are back on the update train
+ updateProvidersLocked();
}
}
@@ -587,7 +591,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
- private boolean isAllowedBySettingsLocked(String provider) {
+ private boolean isAllowedBySettingsLocked(String provider, int userId) {
+ if (userId != mCurrentUserId) {
+ return false;
+ }
if (mEnabledProviders.contains(provider)) {
return true;
}
@@ -597,7 +604,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Use system settings
ContentResolver resolver = mContext.getContentResolver();
- return Settings.Secure.isLocationProviderEnabled(resolver, provider);
+ return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
}
/**
@@ -695,24 +702,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public List getProviders(Criteria criteria, boolean enabledOnly) {
ArrayList out;
- synchronized (mLock) {
- out = new ArrayList(mProviders.size());
- for (LocationProviderInterface provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
- }
- if (isAllowedProviderSafe(name)) {
- if (enabledOnly && !isAllowedBySettingsLocked(name)) {
+ int callingUserId = UserHandle.getCallingUserId();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ out = new ArrayList(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
}
- if (criteria != null && !LocationProvider.propertiesMeetCriteria(
- name, provider.getProperties(), criteria)) {
- continue;
+ if (isAllowedProviderSafe(name)) {
+ if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
+ continue;
+ }
+ if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ name, provider.getProperties(), criteria)) {
+ continue;
+ }
+ out.add(name);
}
- out.add(name);
}
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
if (D) Log.d(TAG, "getProviders()=" + out);
@@ -778,12 +791,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
LocationProviderInterface p = mProviders.get(i);
boolean isEnabled = p.isEnabled();
String name = p.getName();
- boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
+ boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId);
if (isEnabled && !shouldBeEnabled) {
- updateProviderListenersLocked(name, false);
+ updateProviderListenersLocked(name, false, mCurrentUserId);
changesMade = true;
} else if (!isEnabled && shouldBeEnabled) {
- updateProviderListenersLocked(name, true);
+ updateProviderListenersLocked(name, true, mCurrentUserId);
changesMade = true;
}
}
@@ -793,7 +806,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private void updateProviderListenersLocked(String provider, boolean enabled) {
+ private void updateProviderListenersLocked(String provider, boolean enabled, int userId) {
int listeners = 0;
LocationProviderInterface p = mProvidersByName.get(provider);
@@ -806,14 +819,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
final int N = records.size();
for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
- // Sends a notification message to the receiver
- if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList();
+ if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+ // Sends a notification message to the receiver
+ if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList();
+ }
+ deadReceivers.add(record.mReceiver);
}
- deadReceivers.add(record.mReceiver);
+ listeners++;
}
- listeners++;
}
}
@@ -843,12 +858,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (records != null) {
for (UpdateRecord record : records) {
- LocationRequest locationRequest = record.mRequest;
-
- providerRequest.locationRequests.add(locationRequest);
- if (locationRequest.getInterval() < providerRequest.interval) {
- providerRequest.reportLocation = true;
- providerRequest.interval = locationRequest.getInterval();
+ if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+ LocationRequest locationRequest = record.mRequest;
+ providerRequest.locationRequests.add(locationRequest);
+ if (locationRequest.getInterval() < providerRequest.interval) {
+ providerRequest.reportLocation = true;
+ providerRequest.interval = locationRequest.getInterval();
+ }
}
}
@@ -860,9 +876,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// under that threshold.
long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
for (UpdateRecord record : records) {
- LocationRequest locationRequest = record.mRequest;
- if (locationRequest.getInterval() <= thresholdInterval) {
- worksource.add(record.mReceiver.mUid);
+ if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+ LocationRequest locationRequest = record.mRequest;
+ if (locationRequest.getInterval() <= thresholdInterval) {
+ worksource.add(record.mReceiver.mUid);
+ }
}
}
}
@@ -1084,7 +1102,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
oldRecord.disposeLocked(false);
}
- boolean isProviderEnabled = isAllowedBySettingsLocked(name);
+ boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
if (isProviderEnabled) {
applyRequirementsLocked(name);
} else {
@@ -1141,7 +1159,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// update provider
for (String provider : providers) {
// If provider is already disabled, don't need to do anything
- if (!isAllowedBySettingsLocked(provider)) {
+ if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) {
continue;
}
@@ -1156,36 +1174,41 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
String perm = checkPermissionAndRequest(request);
checkPackageName(packageName);
- if (mBlacklist.isBlacklisted(packageName)) {
- if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
- packageName);
- return null;
- }
-
- synchronized (mLock) {
- // Figure out the provider. Either its explicitly request (deprecated API's),
- // or use the fused provider
- String name = request.getProvider();
- if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProviderInterface provider = mProvidersByName.get(name);
- if (provider == null) return null;
-
- if (!isAllowedBySettingsLocked(name)) return null;
-
- Location location = mLastLocation.get(name);
- if (location == null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (mBlacklist.isBlacklisted(packageName)) {
+ if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
+ packageName);
return null;
}
- if (ACCESS_FINE_LOCATION.equals(perm)) {
- return location;
- } else {
- Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
- if (noGPSLocation != null) {
- return mLocationFudger.getOrCreate(noGPSLocation);
+
+ synchronized (mLock) {
+ // Figure out the provider. Either its explicitly request (deprecated API's),
+ // or use the fused provider
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) return null;
+
+ if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null;
+
+ Location location = mLastLocation.get(name);
+ if (location == null) {
+ return null;
+ }
+ if (ACCESS_FINE_LOCATION.equals(perm)) {
+ return location;
+ } else {
+ Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
+ if (noGPSLocation != null) {
+ return mLocationFudger.getOrCreate(noGPSLocation);
+ }
}
}
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- return null;
}
@Override
@@ -1321,11 +1344,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
"\" provider requires ACCESS_FINE_LOCATION permission");
}
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) return false;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return false;
- return isAllowedBySettingsLocked(provider);
+ return isAllowedBySettingsLocked(provider, mCurrentUserId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -1458,6 +1486,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
+ int receiverUserId = UserHandle.getUserId(receiver.mUid);
+ if (receiverUserId != mCurrentUserId) {
+ if (D) {
+ Log.d(TAG, "skipping loc update for background user " + receiverUserId +
+ " (current user: " + mCurrentUserId + ", app: " +
+ receiver.mPackageName + ")");
+ }
+ continue;
+ }
+
if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
receiver.mPackageName);
@@ -1548,7 +1586,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
synchronized (mLock) {
- if (isAllowedBySettingsLocked(provider)) {
+ if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
handleLocationChangedLocked(location, passive);
}
}
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index e99949b38c02a..5598b0ad38213 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.os.Handler;
import android.os.IBinder;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.content.PackageMonitor;
@@ -58,15 +59,17 @@ public class ServiceWatcher implements ServiceConnection {
private IBinder mBinder; // connected service
private String mPackageName; // current best package
private int mVersion; // current best version
+ private int mCurrentUserId;
public ServiceWatcher(Context context, String logTag, String action,
- List initialPackageNames, Runnable newServiceWork, Handler handler) {
+ List initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
mContext = context;
mTag = logTag;
mAction = action;
mPm = mContext.getPackageManager();
mNewServiceWork = newServiceWork;
mHandler = handler;
+ mCurrentUserId = userId;
mSignatureSets = new ArrayList>();
for (int i=0; i < initialPackageNames.size(); i++) {
@@ -85,9 +88,11 @@ public class ServiceWatcher implements ServiceConnection {
}
public boolean start() {
- if (!bindBestPackage(null)) return false;
+ synchronized (mLock) {
+ if (!bindBestPackageLocked(null)) return false;
+ }
- mPackageMonitor.register(mContext, null, true);
+ mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
return true;
}
@@ -98,13 +103,13 @@ public class ServiceWatcher implements ServiceConnection {
* is null.
* Return true if a new package was found to bind to.
*/
- private boolean bindBestPackage(String justCheckThisPackage) {
+ private boolean bindBestPackageLocked(String justCheckThisPackage) {
Intent intent = new Intent(mAction);
if (justCheckThisPackage != null) {
intent.setPackage(justCheckThisPackage);
}
- List rInfos = mPm.queryIntentServices(new Intent(mAction),
- PackageManager.GET_META_DATA);
+ List rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
+ PackageManager.GET_META_DATA, mCurrentUserId);
int bestVersion = Integer.MIN_VALUE;
String bestPackage = null;
for (ResolveInfo rInfo : rInfos) {
@@ -141,36 +146,32 @@ public class ServiceWatcher implements ServiceConnection {
(bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
if (bestPackage != null) {
- bindToPackage(bestPackage, bestVersion);
+ bindToPackageLocked(bestPackage, bestVersion);
return true;
}
return false;
}
- private void unbind() {
+ private void unbindLocked() {
String pkg;
- synchronized (mLock) {
- pkg = mPackageName;
- mPackageName = null;
- mVersion = Integer.MIN_VALUE;
- }
+ pkg = mPackageName;
+ mPackageName = null;
+ mVersion = Integer.MIN_VALUE;
if (pkg != null) {
if (D) Log.d(mTag, "unbinding " + pkg);
mContext.unbindService(this);
}
}
- private void bindToPackage(String packageName, int version) {
- unbind();
+ private void bindToPackageLocked(String packageName, int version) {
+ unbindLocked();
Intent intent = new Intent(mAction);
intent.setPackage(packageName);
- synchronized (mLock) {
- mPackageName = packageName;
- mVersion = version;
- }
+ mPackageName = packageName;
+ mVersion = version;
if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE);
+ | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
}
private boolean isSignatureMatch(Signature[] signatures) {
@@ -197,31 +198,37 @@ public class ServiceWatcher implements ServiceConnection {
*/
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
- if (packageName.equals(mPackageName)) {
- // package updated, make sure to rebind
- unbind();
+ synchronized (mLock) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbindLocked();
+ }
+ // check the updated package in case it is better
+ bindBestPackageLocked(packageName);
}
- // check the updated package in case it is better
- bindBestPackage(packageName);
}
@Override
public void onPackageAdded(String packageName, int uid) {
- if (packageName.equals(mPackageName)) {
- // package updated, make sure to rebind
- unbind();
+ synchronized (mLock) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbindLocked();
+ }
+ // check the new package is case it is better
+ bindBestPackageLocked(packageName);
}
- // check the new package is case it is better
- bindBestPackage(packageName);
}
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (packageName.equals(mPackageName)) {
- unbind();
- // the currently bound package was removed,
- // need to search for a new package
- bindBestPackage(null);
+ synchronized (mLock) {
+ if (packageName.equals(mPackageName)) {
+ unbindLocked();
+ // the currently bound package was removed,
+ // need to search for a new package
+ bindBestPackageLocked(null);
+ }
}
}
};
@@ -271,4 +278,12 @@ public class ServiceWatcher implements ServiceConnection {
return mBinder;
}
}
+
+ public void switchUser(int userId) {
+ synchronized (mLock) {
+ unbindLocked();
+ mCurrentUserId = userId;
+ bindBestPackageLocked(null);
+ }
+ }
}
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 7d030e91d5454..f5cc59f60a2cf 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -21,6 +21,7 @@ import android.location.Address;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import com.android.server.ServiceWatcher;
@@ -38,8 +39,8 @@ public class GeocoderProxy {
private final ServiceWatcher mServiceWatcher;
public static GeocoderProxy createAndBind(Context context,
- List initialPackageNames) {
- GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames);
+ List initialPackageNames, int userId) {
+ GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, userId);
if (proxy.bind()) {
return proxy;
} else {
@@ -47,11 +48,11 @@ public class GeocoderProxy {
}
}
- public GeocoderProxy(Context context, List initialPackageNames) {
+ public GeocoderProxy(Context context, List initialPackageNames, int userId) {
mContext = context;
mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
- null, null);
+ null, null, userId);
}
private boolean bind () {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index a254d748682cb..c272da4b420f1 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -783,6 +783,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
}
+ @Override
+ public void switchUser(int userId) {
+ // nothing to do here
+ }
+
private void handleSetRequest(ProviderRequest request, WorkSource source) {
if (DEBUG) Log.d(TAG, "setRequest " + request);
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 6f0923233e196..80e71f179ed42 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -38,6 +38,8 @@ public interface LocationProviderInterface {
public boolean isEnabled();
public void setRequest(ProviderRequest request, WorkSource source);
+ public void switchUser(int userId);
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
// --- deprecated (but still supported) ---
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 7faf72c33284b..dd2e71c03c6f0 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -25,6 +25,7 @@ import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Log;
@@ -54,9 +55,9 @@ public class LocationProviderProxy implements LocationProviderInterface {
private WorkSource mWorksource = new WorkSource();
public static LocationProviderProxy createAndBind(Context context, String name, String action,
- List initialPackageNames, Handler handler) {
+ List initialPackageNames, Handler handler, int userId) {
LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
- initialPackageNames, handler);
+ initialPackageNames, handler, userId);
if (proxy.bind()) {
return proxy;
} else {
@@ -65,11 +66,11 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
private LocationProviderProxy(Context context, String name, String action,
- List initialPackageNames, Handler handler) {
+ List initialPackageNames, Handler handler, int userId) {
mContext = context;
mName = name;
mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
- mNewServiceWork, handler);
+ mNewServiceWork, handler, userId);
}
private boolean bind () {
@@ -210,6 +211,11 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
}
+ @Override
+ public void switchUser(int userId) {
+ mServiceWatcher.switchUser(userId);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.append("REMOTE SERVICE");
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 36c43ff1ce1dc..1194cbcd2d0d0 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -155,6 +155,11 @@ public class MockProvider implements LocationProviderInterface {
@Override
public void setRequest(ProviderRequest request, WorkSource source) { }
+ @Override
+ public void switchUser(int userId) {
+ // nothing to do here
+ }
+
@Override
public boolean sendExtraCommand(String command, Bundle extras) {
return false;
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 71bae07680a3f..734c572397a47 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -96,6 +96,11 @@ public class PassiveProvider implements LocationProviderInterface {
mReportLocation = request.reportLocation;
}
+ @Override
+ public void switchUser(int userId) {
+ // nothing to do here
+ }
+
public void updateLocation(Location location) {
if (mReportLocation) {
try {
From af75948cddd40a0fde5af051b700306744eae64c Mon Sep 17 00:00:00 2001
From: Christopher Tate
Date: Mon, 8 Oct 2012 14:56:52 -0700
Subject: [PATCH 05/12] New command line tool: "settings"
Because the usual content provider interface doesn't allow specification of
the target user under which to perform settings reads/writes, this CL introduces
a new command line app specifically for that.
Usage:
settings [--user num] get namespace key
settings [--user num] put namespace key value
If a --user argument is not given, the owner user is targetted.
Bug 7299066
Change-Id: I73108bb76b04fad133cc4e0f218d64490de549f1
---
cmds/settings/Android.mk | 18 ++
cmds/settings/settings | 5 +
.../commands/settings/SettingsCmd.java | 220 ++++++++++++++++++
3 files changed, 243 insertions(+)
create mode 100644 cmds/settings/Android.mk
create mode 100755 cmds/settings/settings
create mode 100644 cmds/settings/src/com/android/commands/settings/SettingsCmd.java
diff --git a/cmds/settings/Android.mk b/cmds/settings/Android.mk
new file mode 100644
index 0000000000000..05deb99f72283
--- /dev/null
+++ b/cmds/settings/Android.mk
@@ -0,0 +1,18 @@
+# Copyright 2011 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := settings
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := settings
+LOCAL_SRC_FILES := settings
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
+
+
diff --git a/cmds/settings/settings b/cmds/settings/settings
new file mode 100755
index 0000000000000..ef459ca7d4c4a
--- /dev/null
+++ b/cmds/settings/settings
@@ -0,0 +1,5 @@
+# Script to start "settings" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/settings.jar
+exec app_process $base/bin com.android.commands.settings.SettingsCmd "$@"
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
new file mode 100644
index 0000000000000..0c69f01246b26
--- /dev/null
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2012 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.commands.settings;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IActivityManager.ContentProviderHolder;
+import android.content.IContentProvider;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+public final class SettingsCmd {
+ static final String TAG = "settings";
+
+ enum CommandVerb {
+ UNSPECIFIED,
+ GET,
+ PUT
+ }
+
+ static String[] mArgs;
+ int mNextArg;
+ int mUser = -1; // unspecified
+ CommandVerb mVerb = CommandVerb.UNSPECIFIED;
+ String mTable = null;
+ String mKey = null;
+ String mValue = null;
+
+ public static void main(String[] args) {
+ if (args == null || args.length < 3) {
+ printUsage();
+ return;
+ }
+
+ mArgs = args;
+ try {
+ new SettingsCmd().run();
+ } catch (Exception e) {
+ System.err.println("Unable to run settings command");
+ }
+ }
+
+ public void run() {
+ boolean valid = false;
+ String arg;
+ try {
+ while ((arg = nextArg()) != null) {
+ if ("--user".equals(arg)) {
+ if (mUser != -1) {
+ // --user specified more than once; invalid
+ break;
+ }
+ mUser = Integer.parseInt(nextArg());
+ } else if (mVerb == CommandVerb.UNSPECIFIED) {
+ if ("get".equalsIgnoreCase(arg)) {
+ mVerb = CommandVerb.GET;
+ } else if ("put".equalsIgnoreCase(arg)) {
+ mVerb = CommandVerb.PUT;
+ } else {
+ // invalid
+ System.err.println("Invalid command: " + arg);
+ break;
+ }
+ } else if (mTable == null) {
+ if (!"system".equalsIgnoreCase(arg)
+ && !"secure".equalsIgnoreCase(arg)
+ && !"global".equalsIgnoreCase(arg)) {
+ System.err.println("Invalid namespace '" + arg + "'");
+ break; // invalid
+ }
+ mTable = arg.toLowerCase();
+ } else if (mVerb == CommandVerb.GET) {
+ mKey = arg;
+ if (mNextArg >= mArgs.length) {
+ valid = true;
+ } else {
+ System.err.println("Too many arguments");
+ }
+ break;
+ } else if (mKey == null) {
+ mKey = arg;
+ // keep going; there's another PUT arg
+ } else { // PUT, final arg
+ mValue = arg;
+ if (mNextArg >= mArgs.length) {
+ valid = true;
+ } else {
+ System.err.println("Too many arguments");
+ }
+ break;
+ }
+ }
+ } catch (Exception e) {
+ valid = false;
+ }
+
+ if (valid) {
+ if (mUser < 0) {
+ mUser = UserHandle.USER_OWNER;
+ }
+
+ try {
+ IActivityManager activityManager = ActivityManagerNative.getDefault();
+ IContentProvider provider = null;
+ IBinder token = new Binder();
+ try {
+ ContentProviderHolder holder = activityManager.getContentProviderExternal(
+ "settings", UserHandle.USER_OWNER, token);
+ if (holder == null) {
+ throw new IllegalStateException("Could not find settings provider");
+ }
+ provider = holder.provider;
+
+ switch (mVerb) {
+ case GET:
+ System.out.println(getForUser(provider, mUser, mTable, mKey));
+ break;
+ case PUT:
+ putForUser(provider, mUser, mTable, mKey, mValue);
+ break;
+ default:
+ System.err.println("Unspecified command");
+ break;
+ }
+
+ } finally {
+ if (provider != null) {
+ activityManager.removeContentProviderExternal("settings", token);
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("Error while accessing settings provider");
+ e.printStackTrace();
+ }
+
+ } else {
+ printUsage();
+ }
+ }
+
+ private String nextArg() {
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ mNextArg++;
+ return arg;
+ }
+
+ String getForUser(IContentProvider provider, int userHandle,
+ final String table, final String key) {
+ final String callGetCommand;
+ if ("system".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SYSTEM;
+ else if ("secure".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SECURE;
+ else if ("global".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_GLOBAL;
+ else {
+ System.err.println("Invalid table; no put performed");
+ throw new IllegalArgumentException("Invalid table " + table);
+ }
+
+ String result = null;
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+ Bundle b = provider.call(callGetCommand, key, arg);
+ if (b != null) {
+ result = b.getPairValue();
+ }
+ } catch (RemoteException e) {
+ System.err.println("Can't read key " + key + " in " + table + " for user " + userHandle);
+ }
+ return result;
+ }
+
+ void putForUser(IContentProvider provider, int userHandle,
+ final String table, final String key, final String value) {
+ final String callPutCommand;
+ if ("system".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM;
+ else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE;
+ else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL;
+ else {
+ System.err.println("Invalid table; no put performed");
+ return;
+ }
+
+ try {
+ Bundle arg = new Bundle();
+ arg.putString(Settings.NameValueTable.VALUE, value);
+ arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+ provider.call(callPutCommand, key, arg);
+ } catch (RemoteException e) {
+ System.err.println("Can't set key " + key + " in " + table + " for user " + userHandle);
+ }
+ }
+
+ private static void printUsage() {
+ System.err.println("usage: settings [--user NUM] get namespace key");
+ System.err.println(" settings [--user NUM] put namespace key value");
+ System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
+ System.err.println("If '--user NUM' is not given, the operations are performed on the owner user.");
+ }
+}
From 1cea2f3d87266a1d1df5d5f25fec2f026ca43206 Mon Sep 17 00:00:00 2001
From: Winson Chung
Date: Mon, 8 Oct 2012 20:42:01 -0700
Subject: [PATCH 06/12] Increasing timeout for screenshot chord in keyguard.
(Bug 6941775)
Change-Id: Ie846ce18878f9597e9f0cd97d78322b59042d43c
---
.../internal/policy/impl/PhoneWindowManager.java | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 3351e6157ec56..9307f37b1bfe8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -447,6 +447,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Screenshot trigger states
// Time to volume and power must be pressed within this interval of each other.
private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
+ // Increase the chord delay when taking a screenshot from the keyguard
+ private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f;
private boolean mScreenshotChordEnabled;
private boolean mVolumeDownKeyTriggered;
private long mVolumeDownKeyTime;
@@ -669,12 +671,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mVolumeDownKeyConsumedByScreenshotChord = true;
cancelPendingPowerKeyAction();
- mHandler.postDelayed(mScreenshotChordLongPress,
- ViewConfiguration.getGlobalActionKeyTimeout());
+ mHandler.postDelayed(mScreenshotChordLongPress, getScreenshotChordLongPressDelay());
}
}
}
+ private long getScreenshotChordLongPressDelay() {
+ if (mKeyguardMediator.isShowing()) {
+ // Double the time it takes to take a screenshot from the keyguard
+ return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
+ ViewConfiguration.getGlobalActionKeyTimeout());
+ } else {
+ return ViewConfiguration.getGlobalActionKeyTimeout();
+ }
+ }
+
private void cancelPendingScreenshotChordAction() {
mHandler.removeCallbacks(mScreenshotChordLongPress);
}
From 7ed267f53852aa18aacb605297e56f88d7c08333 Mon Sep 17 00:00:00 2001
From: Michael Jurka
Date: Tue, 9 Oct 2012 17:03:30 +0200
Subject: [PATCH 07/12] Fix one case where recents is sometimes not visible
Bug: 7299653
---
.../src/com/android/systemui/recent/RecentsActivity.java | 4 ++++
.../src/com/android/systemui/recent/RecentsPanelView.java | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index 140cc8050b6ed..79069b89c42a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -205,4 +205,8 @@ public class RecentsActivity extends Activity {
boolean isForeground() {
return mForeground;
}
+
+ boolean isActivityShowing() {
+ return mShowing;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index ff519960cac8b..8607508b9bce7 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -579,7 +579,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
} else {
mRecentTaskDescriptions.addAll(tasks);
}
- if (((RecentsActivity)mContext).isForeground()) {
+ if (((RecentsActivity) mContext).isActivityShowing()) {
refreshViews();
}
}
From 46c4b4886ae7b3a982cb1803f2b3ed7b7aa5f1c4 Mon Sep 17 00:00:00 2001
From: Dirk Dougherty
Date: Sun, 7 Oct 2012 15:05:07 -0700
Subject: [PATCH 08/12] Doc change: Subscriptions free trials on Google Play.
Change-Id: I981eb94658f9f152d1c1a9915ffaadd63b4a965d
---
.../distribute/googleplay/about/monetizing.jd | 7 ++-
.../play/billing/billing_subscriptions.jd | 43 ++++++++++++++++++-
docs/html/guide/google/play/billing/index.jd | 6 +--
3 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/docs/html/distribute/googleplay/about/monetizing.jd b/docs/html/distribute/googleplay/about/monetizing.jd
index d5c6dfad2df21..47d526629a720 100644
--- a/docs/html/distribute/googleplay/about/monetizing.jd
+++ b/docs/html/distribute/googleplay/about/monetizing.jd
@@ -76,7 +76,7 @@ games, or other content.
The payment methods available to users worldwide may vary, based on
location, carrier network, and other factors.
-
+
@@ -97,7 +97,7 @@ targeted advertising.
- Free (no charge to download)
- Priced (user charged before download)
-- In-App products and subscriptions
+- In-app products and subscriptions
@@ -111,6 +111,9 @@ could set a nominal price for your app at download and sell value add-ons,
gameplay levels, and upgrades as in-app products. The only restriction is that
free apps must remain free (to download) for the life of the app.
+For details about in-app products or subscriptions,
+see Google Play In-app Billing.
+
Flexible pricing in the currencies of your customers
diff --git a/docs/html/guide/google/play/billing/billing_subscriptions.jd b/docs/html/guide/google/play/billing/billing_subscriptions.jd
index ae129511a2e3b..68eda196e3fec 100755
--- a/docs/html/guide/google/play/billing/billing_subscriptions.jd
+++ b/docs/html/guide/google/play/billing/billing_subscriptions.jd
@@ -12,6 +12,7 @@ parent.link=index.html
Subscription publishing and unpublishing
Subscription pricing
User billing
+
Free trial period
Subscription cancellation
App uninstallation
Refunds
@@ -94,8 +95,10 @@ digital content, from any type of app or game.
As with other in-app products, you configure and publish subscriptions using
the Developer Console and then sell them from inside apps installed on an
Android-powered devices. In the Developer console, you create subscription
-products and add them to a product list, setting a price for each, choosing a
-billing interval of monthly or annually, and then publishing. In your apps, it’s
+products and add them to a product list, then set a price and optional trial
+period for each, choose a billing interval (monthly or annual), and then publish.
+
+
In your apps, it’s
straightforward to add support for subscription purchases. The implementation
extends the standard In-app Billing API to support a new product type but uses
the same communication model, data structures, and user interactions as for
@@ -145,6 +148,7 @@ subscription and if so, allow access to your content.
You can set up subscriptions with either monthly or annual billing
You can sell multiple subscription items in an app with various billing
intervals or prices, such as for promotions
+
You can offer a configurable trial period for any subscription. New!
Users purchase your subscriptions from inside your apps, rather than
directly from Google Play
Users manage their purchased subscriptions from the My Apps screen in
@@ -251,6 +255,41 @@ notify your backend servers of subscription purchases, tokens, and any billing
errors that may occur. Your backend servers can use the server-side API to query
and update your records and follow up with customers directly, if needed.
+Free Trial Period
+
+For any subscription, you can set up a free trial period that lets users
+try your subscription content before buying it. The trial period
+runs for the period of time that you set and then automatically converts to a full subscription
+managed according to the subscription's billing interval and price.
+
+To take advantage of a free trial, a user must "purchase" the full
+subscription through the standard In-app Billing flow, providing a valid form of
+payment to use for billing and completing the normal purchase transaction.
+However, the user is not charged any money, since the initial period corresponds
+to the free trial. Instead, Google Play records a transaction of $0.00 and the
+subscription is marked as purchased for the duration of the trial period or
+until cancellation. When the transaction is complete, Google Play notifies users
+by email that they have purchased a subscription that includes a free trial
+period and that the initial charge was $0.00.
+
+When the trial period ends, Google Play automatically initiates billing
+against the credit card that the user provided during the initial purchase, at the amount set
+for the full subscription, and continuing at the subscription interval. If
+necessary, the user can cancel the subscription at any time during the trial
+period. In this case, Google Play marks the subscription as expired immediately,
+rather than waiting until the end of the trial period. The user has not
+paid for the trial period and so is not entitled to continued access after
+cancellation.
+
+You can set up a trial period for a subscription in the Developer Console,
+without needing to modify or update your APK. Just locate and edit the
+subscription in your product list, set a valid number of days for the trial
+(must be 7 days or longer), and publish. You can change the period any time,
+although note that Google Play does not apply the change to users who have
+already "purchased" a trial period for the subscription. Only new subscription
+purchases will use the updated trial period. You can create one free trial
+period per subscription product.
+
Subscription cancellation
Users can view the status of all of their subscriptions and cancel them if
diff --git a/docs/html/guide/google/play/billing/index.jd b/docs/html/guide/google/play/billing/index.jd
index a33b19961f130..134140d3d62d5 100755
--- a/docs/html/guide/google/play/billing/index.jd
+++ b/docs/html/guide/google/play/billing/index.jd
@@ -42,10 +42,8 @@ and features, and more. You can use In-app Billing to sell products as
-
Support for subscriptions New!
-
In-app Billing now lets you sell subscriptions in your apps, as well as standard in-app products.
- For details on how to sell subscriptions to content, services, and features, see the
- Subscriptions documentation.
+
Free trials for subscriptions New!
+
You can now offer users a configurable free trial period for your in-app subscriptions. You can set up trials with a simple change in the Developer Console—no change to your app code is needed.
From 462ff630e441f5bc36c4b926ce81bdb35fe7d04b Mon Sep 17 00:00:00 2001
From: Irfan Sheriff
Date: Tue, 9 Oct 2012 10:52:57 -0700
Subject: [PATCH 09/12] Fix handling of hidden access points
We now get raw hex data from the supplicant and we convert it into
printable format.
For hidden access point, we always used to return a single empty string.
We need to make sure we maintain that behavior for apps to not start
displaying empty strings.
Bug: 7310749
Change-Id: I2599b9b5e15be91fc34e9af629ad893b1a0357fc
---
wifi/java/android/net/wifi/WifiSsid.java | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 6f361119131db..3e5f10f1f8030 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -156,7 +156,11 @@ public class WifiSsid implements Parcelable {
@Override
public String toString() {
- if (octets.size() <= 0) return "";
+ byte[] ssidBytes = octets.toByteArray();
+ // Supplicant returns \x00\x00\x00\x00\x00\x00\x00\x00 hex string
+ // for a hidden access point. Make sure we maintain the previous
+ // behavior of returning empty string for this case.
+ if (octets.size() <= 0 || isArrayAllZeroes(ssidBytes)) return "";
// TODO: Handle conversion to other charsets upon failure
Charset charset = Charset.forName("UTF-8");
CharsetDecoder decoder = charset.newDecoder()
@@ -164,7 +168,7 @@ public class WifiSsid implements Parcelable {
.onUnmappableCharacter(CodingErrorAction.REPLACE);
CharBuffer out = CharBuffer.allocate(32);
- CoderResult result = decoder.decode(ByteBuffer.wrap(octets.toByteArray()), out, true);
+ CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true);
out.flip();
if (result.isError()) {
return NONE;
@@ -172,6 +176,13 @@ public class WifiSsid implements Parcelable {
return out.toString();
}
+ private boolean isArrayAllZeroes(byte[] ssidBytes) {
+ for (int i = 0; i< ssidBytes.length; i++) {
+ if (ssidBytes[i] != 0) return false;
+ }
+ return true;
+ }
+
/** @hide */
public byte[] getOctets() {
return octets.toByteArray();
From 3307958c6b4b3707c8861db829893b1f5820b677 Mon Sep 17 00:00:00 2001
From: Adam Powell
Date: Tue, 9 Oct 2012 11:20:39 -0700
Subject: [PATCH 10/12] Allow more scaling values to be tuned in resource
overlays
Bug 7267507
Change-Id: I3fba6882a0ac4cb7d34430ba36dec83c030a73cd
---
core/java/android/view/ScaleGestureDetector.java | 9 ++++++---
core/res/res/values/config.xml | 4 ++++
core/res/res/values/symbols.xml | 1 +
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index a74e438c07f4c..ee3f5d8e266b3 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -17,6 +17,7 @@
package android.view;
import android.content.Context;
+import android.content.res.Resources;
import android.os.SystemClock;
import android.util.FloatMath;
@@ -162,9 +163,11 @@ public class ScaleGestureDetector {
mContext = context;
mListener = listener;
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
- mTouchMinMajor =
- (int) (context.getResources().getDisplayMetrics().density * TOUCH_MIN_MAJOR + 0.5f);
- mMinSpan = context.getResources().getDimensionPixelSize(
+
+ final Resources res = context.getResources();
+ mTouchMinMajor = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_minScalingTouchMajor);
+ mMinSpan = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_minScalingSpan);
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16960c8737039..06560b23ab1e7 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -941,6 +941,10 @@
reported by the hardware. -->
27mm
+
+ 48dp
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 281d92ade7357..f4a13c119aacf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1149,6 +1149,7 @@
+
From 901ac560cafe0b8d76fd7441cbaf8749be33577c Mon Sep 17 00:00:00 2001
From: Robert Greenwalt
Date: Mon, 8 Oct 2012 10:13:10 -0700
Subject: [PATCH 11/12] Add config resource to help ATT prepaid
bug:7306353
Change-Id: I8da9e7591ad5e28d5b319df11435333da44fb7d3
---
core/res/res/values/config.xml | 5 +++++
core/res/res/values/symbols.xml | 1 +
2 files changed, 6 insertions(+)
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a00f071fd548c..15de5f85797e2 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -984,4 +984,9 @@
false
+
+ false
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e615fd40ae4d1..3121067736d9f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -276,6 +276,7 @@
+
From 36070b414a710dd09a24698699f501d58c5772a4 Mon Sep 17 00:00:00 2001
From: rich cannings
Date: Tue, 9 Oct 2012 11:50:15 -0700
Subject: [PATCH 12/12] Fix verfication bypassed for adb installs
ADB installs appear as UserHandle.USER_ALL, and can only be performed by
UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
This returns a valid userId to call PackageManagerService.getPackageUid.
Bug: 7293091
Change-Id: I7a5497cfe5fa2e7aa804345cf9f507ec26a0db21
---
.../com/android/server/pm/PackageManagerService.java | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 0600f5c89d5d2..b8d728684874a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -6374,12 +6374,22 @@ public class PackageManagerService extends IPackageManager.Stub {
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ /*
+ * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
+ * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
+ */
+ int userIdentifier = getUser().getIdentifier();
+ if (userIdentifier == UserHandle.USER_ALL
+ && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) {
+ userIdentifier = UserHandle.USER_OWNER;
+ }
+
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
- : getPackageUid(mRequiredVerifierPackage, getUser().getIdentifier());
+ : getPackageUid(mRequiredVerifierPackage, userIdentifier);
if (requiredUid != -1 && isVerificationEnabled(flags)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);