}
}
+
+ //==========================================================================================
+ // Safe media volume management.
+ // MUSIC stream volume level is limited when headphones are connected according to safety
+ // regulation. When the user attempts to raise the volume above the limit, a warning is
+ // displayed and the user has to acknowlegde before the volume is actually changed.
+ // The volume index corresponding to the limit is stored in config_safe_media_volume_index
+ // property. Platforms with a different limit must set this property accordingly in their
+ // overlay.
+ //==========================================================================================
+
+ // mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
+ // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
+ // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
+ // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
+ // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
+ // (when user opts out).
+ private final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
+ private final int SAFE_MEDIA_VOLUME_DISABLED = 1;
+ private final int SAFE_MEDIA_VOLUME_INACTIVE = 2;
+ private final int SAFE_MEDIA_VOLUME_ACTIVE = 3;
+ private Integer mSafeMediaVolumeState;
+
+ private int mMcc = 0;
+ // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
+ private int mSafeMediaVolumeIndex;
+ // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
+ private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
+ // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
+ // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
+ // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
+ private int mMusicActiveMs;
+ private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
+ private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
+ private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
+
+ private void setSafeMediaVolumeEnabled(boolean on) {
+ synchronized (mSafeMediaVolumeState) {
+ if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
+ (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
+ if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
+ mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
+ enforceSafeMediaVolume();
+ } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
+ mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
+ mMusicActiveMs = 0;
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MUSIC_ACTIVE,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ MUSIC_ACTIVE_POLL_PERIOD_MS);
+ }
+ }
+ }
+ }
+
+ private void enforceSafeMediaVolume() {
+ VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
+ boolean lastAudible = (streamState.muteCount() != 0);
+ int devices = mSafeMediaVolumeDevices;
+ int i = 0;
+
+ while (devices != 0) {
+ int device = 1 << i++;
+ if ((device & devices) == 0) {
+ continue;
+ }
+ int index = streamState.getIndex(device, lastAudible);
+ if (index > mSafeMediaVolumeIndex) {
+ if (lastAudible) {
+ streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device);
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_QUEUE,
+ PERSIST_LAST_AUDIBLE,
+ device,
+ streamState,
+ PERSIST_DELAY);
+ } else {
+ streamState.setIndex(mSafeMediaVolumeIndex, device, true);
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_QUEUE,
+ device,
+ 0,
+ streamState,
+ 0);
+ }
+ }
+ devices &= ~device;
+ }
+ }
+
+ private boolean checkSafeMediaVolume(int streamType, int index, int device) {
+ synchronized (mSafeMediaVolumeState) {
+ if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
+ (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
+ ((device & mSafeMediaVolumeDevices) != 0) &&
+ (index > mSafeMediaVolumeIndex)) {
+ mVolumePanel.postDisplaySafeVolumeWarning();
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public void disableSafeMediaVolume() {
+ synchronized (mSafeMediaVolumeState) {
+ setSafeMediaVolumeEnabled(false);
+ }
+ }
+
+
+ //==========================================================================================
+ // 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));
+ }
+
+ public int verifyX509CertChain(int numcerts, byte [] chain, String domain, String authType) {
+
+ if (DEBUG_CERTS) {
+ Log.v(TAG, "java side verify for "
+ + numcerts + " certificates (" + chain.length + " bytes"
+ + ")for "+ domain + "/" + authType);
+ }
+
+ byte[][] certChain = new byte[numcerts][];
+
+ ByteBuffer buf = ByteBuffer.wrap(chain);
+ for (int i = 0; i < numcerts; i++) {
+ int certlen = buf.getInt();
+ if (DEBUG_CERTS) {
+ Log.i(TAG, "cert " + i +": " + certlen);
+ }
+ certChain[i] = new byte[certlen];
+ buf.get(certChain[i]);
+ }
+
+ try {
+ SslError err = CertificateChainValidator.verifyServerCertificates(certChain,
+ domain, authType);
+ if (DEBUG_CERTS) {
+ Log.i(TAG, "verified: " + err);
+ }
+ if (err == null) {
+ return -1;
+ } else {
+ return err.getPrimaryError();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "failed to verify chain: " + e);
+ }
+ return SslError.SSL_INVALID;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);