Merge "MediaSync: fix message delay based on play time for pending audio frames."
diff --git a/api/current.txt b/api/current.txt
index d29bd78..61e9186 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14924,6 +14924,7 @@
public class AudioRecord {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -14933,7 +14934,9 @@
method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
+ method public android.media.AudioDeviceInfo getPreferredInputDevice();
method public int getRecordingState();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int read(byte[], int, int);
@@ -14944,8 +14947,10 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
+ method public boolean setPreferredInputDevice(android.media.AudioDeviceInfo);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener, android.os.Handler);
method public void startRecording() throws java.lang.IllegalStateException;
@@ -14986,6 +14991,7 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -15004,6 +15010,7 @@
method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int getStreamType();
@@ -15012,6 +15019,7 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -16425,6 +16433,14 @@
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -30102,8 +30118,8 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
- field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
public static abstract class Call.Callback {
@@ -30163,20 +30179,6 @@
field public static final int CONFERENCE = 1; // 0x1
}
- public final class CallState {
- method public static java.lang.String toString(int);
- field public static final int ABORTED = 8; // 0x8
- field public static final int ACTIVE = 5; // 0x5
- field public static final int CONNECTING = 1; // 0x1
- field public static final int DIALING = 3; // 0x3
- field public static final int DISCONNECTED = 7; // 0x7
- field public static final int DISCONNECTING = 9; // 0x9
- field public static final int NEW = 0; // 0x0
- field public static final int ON_HOLD = 6; // 0x6
- field public static final int PRE_DIAL_WAIT = 2; // 0x2
- field public static final int RINGING = 4; // 0x4
- }
-
public final class CameraCapabilities implements android.os.Parcelable {
ctor public CameraCapabilities(int, int);
method public int describeContents();
@@ -30593,6 +30595,7 @@
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+ method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
method public void silenceRinger();
@@ -30657,6 +30660,7 @@
method public android.os.Bundle getConfigForSubId(int);
method public void reloadCarrierConfigForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final java.lang.String BOOL_APN_EXPAND = "bool_apn_expand";
field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
diff --git a/api/system-current.txt b/api/system-current.txt
index d6242ae..e9f2f9a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16135,6 +16135,7 @@
public class AudioRecord {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
@@ -16144,7 +16145,9 @@
method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
+ method public android.media.AudioDeviceInfo getPreferredInputDevice();
method public int getRecordingState();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int read(byte[], int, int);
@@ -16155,8 +16158,10 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
+ method public boolean setPreferredInputDevice(android.media.AudioDeviceInfo);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener, android.os.Handler);
method public void startRecording() throws java.lang.IllegalStateException;
@@ -16199,6 +16204,7 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
@@ -16217,6 +16223,7 @@
method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int getStreamType();
@@ -16225,6 +16232,7 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -17641,6 +17649,14 @@
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -32208,8 +32224,9 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
- field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
+ field public static final deprecated int STATE_PRE_DIAL_WAIT = 8; // 0x8
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
public static abstract class Call.Callback {
@@ -32273,20 +32290,6 @@
field public static final int CONFERENCE = 1; // 0x1
}
- public final class CallState {
- method public static java.lang.String toString(int);
- field public static final int ABORTED = 8; // 0x8
- field public static final int ACTIVE = 5; // 0x5
- field public static final int CONNECTING = 1; // 0x1
- field public static final int DIALING = 3; // 0x3
- field public static final int DISCONNECTED = 7; // 0x7
- field public static final int DISCONNECTING = 9; // 0x9
- field public static final int NEW = 0; // 0x0
- field public static final int ON_HOLD = 6; // 0x6
- field public static final int PRE_DIAL_WAIT = 2; // 0x2
- field public static final int RINGING = 4; // 0x4
- }
-
public final class CameraCapabilities implements android.os.Parcelable {
ctor public CameraCapabilities(int, int);
method public int describeContents();
@@ -32742,6 +32745,7 @@
method public boolean isRinging();
method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+ method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
method public void silenceRinger();
@@ -32811,6 +32815,7 @@
method public void reloadCarrierConfigForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final java.lang.String BOOL_APN_EXPAND = "bool_apn_expand";
field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
new file mode 100644
index 0000000..4aed5c1
--- /dev/null
+++ b/core/java/android/annotation/RequiresPermission.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated element requires (or may require) one or more permissions.
+ * <p/>
+ * Example of requiring a single permission:
+ * <pre>{@code
+ * @RequiresPermission(Manifest.permission.SET_WALLPAPER)
+ * public abstract void setWallpaper(Bitmap bitmap) throws IOException;
+ *
+ * @RequiresPermission(ACCESS_COARSE_LOCATION)
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring at least one permission from a set:
+ * <pre>{@code
+ * @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring multiple permissions:
+ * <pre>{@code
+ * @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring separate read and write permissions for a content provider:
+ * <pre>{@code
+ * @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ * @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * }</pre>
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})
+public @interface RequiresPermission {
+ /**
+ * The name of the permission that is required, if precisely one permission
+ * is required. If more than one permission is required, specify either
+ * {@link #allOf()} or {@link #anyOf()} instead.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #allOf()} must both be null.
+ */
+ String value() default "";
+
+ /**
+ * Specifies a list of permission names that are all required.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #value()} must both be null.
+ */
+ String[] allOf() default {};
+
+ /**
+ * Specifies a list of permission names where at least one is required
+ * <p>
+ * If specified, {@link #allOf()} and {@link #value()} must both be null.
+ */
+ String[] anyOf() default {};
+
+ /**
+ * If true, the permission may not be required in all cases (e.g. it may only be
+ * enforced on certain platforms, or for certain call parameters, etc.
+ */
+ boolean conditional() default false;
+
+ /**
+ * Specifies that the given permission is required for read operations
+ */
+ @Target(FIELD)
+ @interface Read {
+ RequiresPermission value();
+ }
+
+ /**
+ * Specifies that the given permission is required for write operations
+ */
+ @Target(FIELD)
+ @interface Write {
+ RequiresPermission value();
+ }
+}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7f062d9..b11c509 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2513,6 +2513,14 @@
return true;
}
+ case UPDATE_DEVICE_OWNER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ updateDeviceOwner(packageName);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -5801,6 +5809,18 @@
}
@Override
+ public void updateDeviceOwner(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(UPDATE_DEVICE_OWNER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public int getPackageProcessState(String packageName) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 7e03faa..00558fe 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -495,6 +495,7 @@
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
+ public void updateDeviceOwner(String packageName) throws RemoteException;
public int getPackageProcessState(String packageName) throws RemoteException;
@@ -837,4 +838,5 @@
int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
+ int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2418e82..79e560f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -792,7 +792,6 @@
// mService is null, handle that case
}
} catch (RemoteException e) {Log.e(TAG, "", e);}
- if (DBG) Log.d(TAG, "" + hashCode() + ": getState() : mService = null. Returning STATE_OFF");
return STATE_OFF;
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 393cf8e..fd65d56 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -26,6 +26,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
@@ -204,8 +205,13 @@
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
- CancellationSignal.fromTransport(cancellationSignal));
+ // The caller has no access to the data, so return an empty cursor with
+ // the columns in the requested order. The caller may ask for an invalid
+ // column and we would not catch that but this is not a problem in practice.
+ // We do not call ContentProvider#query with a modified where clause since
+ // the implementation is not guaranteed to be backed by a SQL database, hence
+ // it may not handle properly the tautology where clause we would have created.
+ return new MatrixCursor(projection, 0);
}
final String original = setCallingPackage(callingPkg);
try {
@@ -817,31 +823,6 @@
}
/**
- * @hide
- * Implementation when a caller has performed a query on the content
- * provider, but that call has been rejected for the operation given
- * to {@link #setAppOps(int, int)}. The default implementation
- * rewrites the <var>selection</var> argument to include a condition
- * that is never true (so will always result in an empty cursor)
- * and calls through to {@link #query(android.net.Uri, String[], String, String[],
- * String, android.os.CancellationSignal)} with that.
- */
- public Cursor rejectQuery(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- CancellationSignal cancellationSignal) {
- // The read is not allowed... to fake it out, we replace the given
- // selection statement with a dummy one that will always be false.
- // This way we will get a cursor back that has the correct structure
- // but contains no rows.
- if (selection == null || selection.isEmpty()) {
- selection = "'A' = 'B'";
- } else {
- selection = "'A' = 'B' AND (" + selection + ")";
- }
- return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
- }
-
- /**
* Implement this to handle query requests from clients.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index b302f95..931cd3e 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,11 +16,13 @@
package android.os;
+import android.provider.DocumentsContract.Document;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.webkit.MimeTypeMap;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -34,6 +36,7 @@
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -533,4 +536,76 @@
}
return null;
}
+
+ /**
+ * Generates a unique file name under the given parent directory. If the display name doesn't
+ * have an extension that matches the requested MIME type, the default extension for that MIME
+ * type is appended. If a file already exists, the name is appended with a numerical value to
+ * make it unique.
+ *
+ * For example, the display name 'example' with 'text/plain' MIME might produce
+ * 'example.txt' or 'example (1).txt', etc.
+ *
+ * @throws FileNotFoundException
+ */
+ public static File buildUniqueFile(File parent, String mimeType, String displayName)
+ throws FileNotFoundException {
+ String name;
+ String ext;
+
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ name = displayName;
+ ext = null;
+ } else {
+ String mimeTypeFromExt;
+
+ // Extract requested extension from display name
+ final int lastDot = displayName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ name = displayName.substring(0, lastDot);
+ ext = displayName.substring(lastDot + 1);
+ mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ ext.toLowerCase());
+ } else {
+ name = displayName;
+ ext = null;
+ mimeTypeFromExt = null;
+ }
+
+ if (mimeTypeFromExt == null) {
+ mimeTypeFromExt = "application/octet-stream";
+ }
+
+ final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+ mimeType);
+ if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+ // Extension maps back to requested MIME type; allow it
+ } else {
+ // No match; insist that create file matches requested MIME
+ name = displayName;
+ ext = extFromMimeType;
+ }
+ }
+
+ File file = buildFile(parent, name, ext);
+
+ // If conflicting file, try adding counter suffix
+ int n = 0;
+ while (file.exists()) {
+ if (n++ >= 32) {
+ throw new FileNotFoundException("Failed to create unique file");
+ }
+ file = buildFile(parent, name + " (" + n + ")", ext);
+ }
+
+ return file;
+ }
+
+ private static File buildFile(File parent, String name, String ext) {
+ if (TextUtils.isEmpty(ext)) {
+ return new File(parent, name);
+ } else {
+ return new File(parent, name + "." + ext);
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8dc27dc..00c851b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5391,6 +5391,12 @@
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the default dialer application
+ * @hide
+ */
+ public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
+
+ /**
* Specifies the package name currently configured to be the emergency assistance application
*
* @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index d21cc3c..f0ca276 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -78,7 +78,8 @@
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the PendingIntent supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -113,7 +114,8 @@
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the IntentSender supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -144,6 +146,32 @@
mIntentSender = intentSender;
}
+ /**
+ * Construct a deep link target for presentation by a chooser UI.
+ *
+ * <p>A target is composed of a title and an icon for presentation to the user.
+ * The UI presenting this target may truncate the title if it is too long to be presented
+ * in the available space, as well as crop, resize or overlay the supplied icon.</p>
+ *
+ * <p>The creator of a target may supply a ranking score. This score is assumed to be relative
+ * to the other targets supplied by the same
+ * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
+ *
+ * <p>Before being sent, the Intent supplied will be
+ * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
+ * to the chooser.</p>
+ *
+ * <p>Take care not to place custom {@link android.os.Parcelable} types into
+ * the Intent as extras, as the system will not be able to unparcel it to merge
+ * additional extras.</p>
+ *
+ * @param title title of this target that will be shown to a user
+ * @param icon icon to represent this target
+ * @param score ranking score for this target between 0.0f and 1.0f, inclusive
+ * @param intent Intent to fill in and send if the user chooses this target
+ */
public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) {
mTitle = title;
mIcon = icon;
@@ -358,6 +386,10 @@
}
dest.writeFloat(mScore);
IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest);
+ dest.writeInt(mIntent != null ? 1 : 0);
+ if (mIntent != null) {
+ mIntent.writeToParcel(dest, 0);
+ }
}
public static final Creator<ChooserTarget> CREATOR
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 943beb0..453e4f5 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -900,7 +900,9 @@
* and therefore secure policy, this setting should be disabled.
* Note that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
@@ -920,7 +922,9 @@
* the value of {@link #getAllowUniversalAccessFromFileURLs} is true.
* Note too, that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8403e77..a6c39e6 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -401,6 +401,11 @@
}
@Override
+ public boolean shouldGetResolvedFilter() {
+ return true;
+ }
+
+ @Override
public int getCount() {
int count = super.getCount();
if (mServiceTargets != null) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3cd69a1..7f51d92 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1062,7 +1062,7 @@
} else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent,
PackageManager.MATCH_DEFAULT_ONLY
- | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0)
);
// Filter out any activities that the launched uid does not
@@ -1188,6 +1188,10 @@
// This space for rent
}
+ public boolean shouldGetResolvedFilter() {
+ return mFilterLastUsed;
+ }
+
private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
CharSequence roLabel) {
// Process labels from start to i
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 579cad4..3a1e0ca 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -433,15 +433,13 @@
mHidden = false;
mDismissed = false;
- cancelAllAnimations();
+ cancelDismissAndHideAnimations();
+ cancelOverflowAnimations();
// Make sure a panel is set as the content.
if (mContentContainer.getChildCount() == 0) {
setMainPanelAsContent();
}
preparePopupContent();
- // If we're yet to show the popup, set the container visibility to zero.
- // The "show" animation will make this visible.
- mContentContainer.setAlpha(0);
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y);
setTouchableSurfaceInsetsComputer();
runShowAnimation();
@@ -451,12 +449,13 @@
* Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
*/
public void dismiss() {
- if (!isShowing()) {
+ if (mDismissed) {
return;
}
mHidden = false;
mDismissed = true;
+ mHideAnimation.cancel();
runDismissAnimation();
setZeroTouchableSurface();
}
@@ -499,7 +498,7 @@
return;
}
- cancelAllAnimations();
+ cancelOverflowAnimations();
preparePopupContent();
mPopupWindow.update(x, y, getWidth(), getHeight());
}
@@ -563,10 +562,12 @@
mHideAnimation.start();
}
- private void cancelAllAnimations() {
- mShowAnimation.cancel();
+ private void cancelDismissAndHideAnimations() {
mDismissAnimation.cancel();
mHideAnimation.cancel();
+ }
+
+ private void cancelOverflowAnimations() {
mOpenOverflowAnimation.cancel();
mCloseOverflowAnimation.cancel();
}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 23b0d50..d86f71a 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -584,6 +584,13 @@
return frameCount * channelCount * audio_bytes_per_sample(format);
}
+static jboolean android_media_AudioRecord_setInputDevice(
+ JNIEnv *env, jobject thiz, jint device_id) {
+
+// sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+// return lpRecorder->setInputDevice(device_id) == NO_ERROR;
+ return false;
+}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -616,6 +623,7 @@
"()I", (void *)android_media_AudioRecord_get_pos_update_period},
{"native_get_min_buff_size",
"(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
+ {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
};
// field names found in android/media/AudioRecord.java
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 5c9e813..ee9e2e4 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,6 +21,7 @@
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import android.content.Context;
+import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
@@ -43,6 +44,8 @@
private File mDir;
private File mTestFile;
private File mCopyFile;
+ private File mTarget;
+
@Override
protected void setUp() throws Exception {
@@ -50,11 +53,15 @@
mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
mTestFile = new File(mDir, "test.file");
mCopyFile = new File(mDir, "copy.file");
+
+ mTarget = getContext().getFilesDir();
+ FileUtils.deleteContents(mTarget);
}
@Override
protected void tearDown() throws Exception {
IoUtils.deleteContents(mDir);
+ FileUtils.deleteContents(mTarget);
}
// TODO: test setPermissions(), getPermissions()
@@ -225,6 +232,63 @@
assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
}
+ public void testBuildUniqueFile_normal() throws Exception {
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
+ assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
+ assertNameEquals("test.png.jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
+ assertNameEquals("test.png.jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png"));
+
+ assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test"));
+ assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac"));
+ assertNameEquals("test.flac",
+ FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test"));
+ assertNameEquals("test.flac",
+ FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
+ }
+
+ public void testBuildUniqueFile_unknown() throws Exception {
+ assertNameEquals("test",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
+ assertNameEquals("test.jpg",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
+ assertNameEquals(".test",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test"));
+
+ assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test"));
+ assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
+ }
+
+ public void testBuildUniqueFile_dir() throws Exception {
+ assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+ new File(mTarget, "test").mkdir();
+ assertNameEquals("test (1)",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+
+ assertNameEquals("test.jpg",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ new File(mTarget, "test.jpg").mkdir();
+ assertNameEquals("test.jpg (1)",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ }
+
+ public void testBuildUniqueFile_increment() throws Exception {
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test.jpg").createNewFile();
+ assertNameEquals("test (1).jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test (1).jpg").createNewFile();
+ assertNameEquals("test (2).jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ }
+
+ private static void assertNameEquals(String expected, File actual) {
+ assertEquals(expected, actual.getName());
+ }
+
private void touch(String name, long age) throws Exception {
final File file = new File(mDir, name);
file.createNewFile();
diff --git a/docs/html/google/play-services/index.jd b/docs/html/google/play-services/index.jd
index e31290a..d674f7f 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -88,7 +88,7 @@
<li><a href="https://developers.google.com/maps/documentation/android/wear"
class="external-link">Google Maps on Android Wear developer guide</a>
</li>
- <li><a href="https://github.com/googlemaps/android-samples"
+ <li><a href="https://github.com/googlemaps/android-samples/tree/master/BasicWearMap"
class="external-link">Google Maps on Android Wear sample</a>
</li>
<li><a href="https://developers.google.com/maps/documentation/android/releases"
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index cbfa82e..743e692 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -401,19 +401,35 @@
format and style your string resources.</p>
-<h3>Escaping apostrophes and quotes</h3>
+<h3 id="escaping_quotes">Escaping apostrophes and quotes</h3>
-<p>If you have an apostrophe or a quote in your string, you must either escape it or enclose the
-whole string in the other type of enclosing quotes. For example, here are some stings that
-do and don't work:</p>
+<p>
+ If you have an apostrophe (<code>'</code>) in your string, you must either
+ escape it with a backslash (<code>\'</code>) or enclose the string in
+ double-quotes (<code>""</code>). For example, here are some strings that do
+ and don't work:
+</p>
<pre>
-<string name="good_example">"This'll work"</string>
-<string name="good_example_2">This\'ll also work</string>
+<string name="good_example">This\'ll work</string>
+<string name="good_example_2">"This'll also work"</string>
<string name="bad_example">This doesn't work</string>
-<string name="bad_example_2">XML encodings don&apos;t work</string>
+ <!-- Causes a compile error -->
</pre>
+<p>
+ If you have a double-quote in your string, you must escape it
+ (<code>\"</code>). Surrounding the string with single-quotes does
+ <em>not</em> work.
+</p>
+
+<pre>
+<string name="good_example">This is a \"good string\".</string>
+<string name="bad_example">This is a "bad string".</string>
+ <!-- Quotes are stripped; displays as: This is a bad string. -->
+<string name="bad_example_2">'This is another "bad string".'</string>
+ <!-- Causes a compile error -->
+</pre>
<h3>Formatting strings</h3>
diff --git a/docs/html/reference/com/google/android/gms/fitness/data/Field.html b/docs/html/reference/com/google/android/gms/fitness/data/Field.html
index 91bf861..0f97e7e 100644
--- a/docs/html/reference/com/google/android/gms/fitness/data/Field.html
+++ b/docs/html/reference/com/google/android/gms/fitness/data/Field.html
@@ -151,7 +151,7 @@
<a name="top"></a>
-<!-- dialog to prompt lang pref change when loaded from hardcoded URL
+<!-- dialog to prompt lang pref change when loaded from hardcoded URL
<div id="langMessage" style="display:none">
<div>
<div class="lang en">
@@ -201,7 +201,7 @@
<div id="header-wrapper">
<div id="header">
-
+
<div class="wrap" id="header-wrap">
@@ -245,8 +245,8 @@
</ul>
-
-
+
+
<div class="menu-container">
<div class="moremenu">
<div id="more-btn"></div>
@@ -267,8 +267,8 @@
<li><a href="http://source.android.com">Android Open Source Project</a></li>
</ul>
-
-
+
+
<div class="header">Language</div>
<div id="language" class="locales">
<select name="language" onChange="changeLangPref(this.value, true)">
@@ -286,8 +286,8 @@
loadLangPref();
//-->
</script>
-
-
+
+
<br class="clearfix" />
</div><!-- end 'mid' -->
<div class="bottom"></div>
@@ -401,10 +401,10 @@
</li>
<li><a href="/google/index.html">Google Services</a>
</li>
-
+
<li><a href="/samples/index.html">Samples</a>
</li>
-
+
</ul>
</li>
<li class="distribute last">
@@ -424,14 +424,14 @@
</div><!-- end header-wrap.wrap -->
</div><!-- end header -->
-
+
<!-- Secondary x-nav -->
<div id="nav-x">
<div class="wrap" style="position:relative;z-index:1">
-
-
-
+
+
+
<ul class="nav-x col-9 develop" style="width:100%">
<li class="training"><a href="/training/index.html"
@@ -469,17 +469,17 @@
<li class="google"><a href="/google/index.html"
>Google Services</a>
</li>
-
+
<li class="samples"><a href="/samples/index.html"
>Samples</a>
</li>
-
+
</ul>
</div>
</div>
<!-- /Sendondary x-nav DEVELOP -->
-
+
<div id="searchResults" class="wrap" style="display:none;">
<h2 id="searchTitle">Results</h2>
@@ -492,7 +492,7 @@
<a class="logo" href="#top"></a>
<a class="top" href="#top"></a>
<ul class="breadcrumb">
-
+
<li class="current">Field</li>
</ul>
</div>
@@ -502,7 +502,7 @@
-
+
<div class="wrap clearfix" id="body-content">
<div class="col-4" id="side-nav" itemscope itemtype="http://schema.org/SiteNavigationElement">
<div id="devdoc-nav" class="scroll-pane">
@@ -759,12 +759,12 @@
</script>
-
+
</div>
<script type="text/javascript">
showGoogleRefTree();
-
+
</script>
</div> <!-- end side-nav -->
<script>
@@ -774,7 +774,7 @@
</script>
-
+
@@ -784,21 +784,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<div class="sum-details-links">
@@ -810,22 +810,22 @@
<a href="#constants">Constants</a>
-
+
| <a href="#inhconstants">Inherited Constants</a>
-
+
| <a href="#lfields">Fields</a>
-
+
| <a href="#pubmethods">Methods</a>
-
+
@@ -835,9 +835,9 @@
</div><!-- end sum-details-links -->
<div class="api-level">
-
-
-
+
+
+
</div>
</div><!-- end api-info-block -->
@@ -847,31 +847,31 @@
<div id="jd-header">
public
-
- final
-
+
+ final
+
class
<h1 itemprop="name">Field</h1>
-
+
extends Object<br/>
-
-
-
-
-
-
- implements
-
- Parcelable
-
-
-
-
+
+
+
+
+ implements
+
+ Parcelable
+
+
+
+
+
+
</div><!-- end header -->
@@ -883,18 +883,18 @@
<tr>
-
+
<td colspan="2" class="jd-inheritance-class-cell">java.lang.Object</td>
</tr>
-
+
<tr>
-
+
<td class="jd-inheritance-space"> ↳</td>
-
+
<td colspan="1" class="jd-inheritance-class-cell">com.google.android.gms.fitness.data.Field</td>
</tr>
-
+
</table>
@@ -962,31 +962,31 @@
<table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_FLOAT">FORMAT_FLOAT</a></td>
<td class="jd-descrcol" width="100%">
Format constant indicating the field holds float values.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_INT32">FORMAT_INT32</a></td>
<td class="jd-descrcol" width="100%">
Format constant indicating the field holds integer values.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_MAP">FORMAT_MAP</a></td>
@@ -1305,33 +1305,33 @@
</div>
<div id="inherited-constants-android.os.Parcelable-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol">CONTENTS_FILE_DESCRIPTOR</td>
<td class="jd-descrcol" width="100%">
-
-
-
+
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol">PARCELABLE_WRITE_RETURN_VALUE</td>
<td class="jd-descrcol" width="100%">
-
-
-
+
+
+
</td>
</tr>
-
-
+
+
</table>
</div>
</div>
@@ -1347,7 +1347,7 @@
<table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1357,13 +1357,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ACCURACY">FIELD_ACCURACY</a></td>
<td class="jd-descrcol" width="100%">
The accuracy of an accompanied value (such as location).
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1373,13 +1373,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ACTIVITY">FIELD_ACTIVITY</a></td>
<td class="jd-descrcol" width="100%">
An activity type of <code><a href="/reference/com/google/android/gms/fitness/FitnessActivities.html">FitnessActivities</a></code>, encoded as an integer for efficiency.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1389,13 +1389,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ALTITUDE">FIELD_ALTITUDE</a></td>
<td class="jd-descrcol" width="100%">
An altitude of a location represented as a float, in meters above sea level.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1405,13 +1405,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_AVERAGE">FIELD_AVERAGE</a></td>
<td class="jd-descrcol" width="100%">
An average value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1421,13 +1421,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_BPM">FIELD_BPM</a></td>
<td class="jd-descrcol" width="100%">
A heart rate in beats per minute.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1437,13 +1437,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_CALORIES">FIELD_CALORIES</a></td>
<td class="jd-descrcol" width="100%">
Calories in kcal.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1469,13 +1469,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_CONFIDENCE">FIELD_CONFIDENCE</a></td>
<td class="jd-descrcol" width="100%">
The confidence of an accompanied value, specified as a value between 0.0 and 100.0.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1485,13 +1485,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_DISTANCE">FIELD_DISTANCE</a></td>
<td class="jd-descrcol" width="100%">
A distance in meters.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1501,13 +1501,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_DURATION">FIELD_DURATION</a></td>
<td class="jd-descrcol" width="100%">
A duration in milliseconds.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1533,13 +1533,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HEIGHT">FIELD_HEIGHT</a></td>
<td class="jd-descrcol" width="100%">
A height in meters.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1549,13 +1549,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HIGH_LATITUDE">FIELD_HIGH_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A high latitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1565,13 +1565,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HIGH_LONGITUDE">FIELD_HIGH_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A high longitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1581,13 +1581,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LATITUDE">FIELD_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A latitude of a location represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1597,13 +1597,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LONGITUDE">FIELD_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A longitude of a location represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1613,13 +1613,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LOW_LATITUDE">FIELD_LOW_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A low latitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1629,13 +1629,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LOW_LONGITUDE">FIELD_LOW_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A low longitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1645,13 +1645,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_MAX">FIELD_MAX</a></td>
<td class="jd-descrcol" width="100%">
A maximum value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1677,13 +1677,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_MIN">FIELD_MIN</a></td>
<td class="jd-descrcol" width="100%">
A minimum value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1725,13 +1725,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_PERCENTAGE">FIELD_PERCENTAGE</a></td>
<td class="jd-descrcol" width="100%">
A percentage value, between 0 and 100.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1741,13 +1741,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_REVOLUTIONS">FIELD_REVOLUTIONS</a></td>
<td class="jd-descrcol" width="100%">
A count of revolutions.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1757,13 +1757,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_RPM">FIELD_RPM</a></td>
<td class="jd-descrcol" width="100%">
Revolutions per minute or rate per minute.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1773,13 +1773,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_SPEED">FIELD_SPEED</a></td>
<td class="jd-descrcol" width="100%">
A speed in meter/sec.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1789,13 +1789,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_STEPS">FIELD_STEPS</a></td>
<td class="jd-descrcol" width="100%">
A count of steps.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1805,13 +1805,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_WATTS">FIELD_WATTS</a></td>
<td class="jd-descrcol" width="100%">
Power in watts.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1821,13 +1821,13 @@
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_WEIGHT">FIELD_WEIGHT</a></td>
<td class="jd-descrcol" width="100%">
A weight in kilograms.
-
-
+
+
</td>
</tr>
-
-
+
+
</table>
@@ -1846,129 +1846,129 @@
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#describeContents()">describeContents</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
boolean</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#equals(java.lang.Object)">equals</a></span>(Object that)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#getFormat()">getFormat</a></span>()</nobr>
-
+
<div class="jd-descrdiv">
Returns the format of the field, as one of the format constant values.
-
-
+
+
</div>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#getName()">getName</a></span>()</nobr>
-
+
<div class="jd-descrdiv">
Returns the name of the field.
-
-
+
+
</div>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#hashCode()">hashCode</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#toString()">toString</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#writeToParcel(android.os.Parcel, int)">writeToParcel</a></span>(Parcel dest, int flags)</nobr>
-
+
</td></tr>
@@ -2003,182 +2003,182 @@
</div>
<div id="inherited-methods-java.lang.Object-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
Object</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">clone</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
boolean</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">equals</span>(Object arg0)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">finalize</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
Class<?></nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">getClass</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">hashCode</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">notify</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">notifyAll</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">toString</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>(long arg0, int arg1)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>(long arg0)</nobr>
-
+
</td></tr>
@@ -2205,7 +2205,7 @@
</div>
<div id="inherited-methods-android.os.Parcelable-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
+
@@ -2232,6 +2232,7 @@
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
@@ -3196,40 +3197,40 @@
<A NAME="NUTRIENT_TOTAL_FAT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_TOTAL_FAT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Total fat in grams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
-
+
"fat.total"
-
+
</span>
</div>
-
+
</div>
</div>
@@ -3237,40 +3238,40 @@
<A NAME="NUTRIENT_TRANS_FAT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_TRANS_FAT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Trans fat in grams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
-
+
"fat.trans"
-
+
</span>
</div>
-
+
</div>
</div>
@@ -3321,31 +3322,31 @@
<A NAME="NUTRIENT_VITAMIN_C"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_VITAMIN_C
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Vitamin C amount in milligrams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
@@ -3372,31 +3373,31 @@
<A NAME="FIELD_ACCURACY"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ACCURACY
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>The accuracy of an accompanied value (such as location).
</p></div>
-
+
</div>
</div>
@@ -3404,33 +3405,33 @@
<A NAME="FIELD_ACTIVITY"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ACTIVITY
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An activity type of <code><a href="/reference/com/google/android/gms/fitness/FitnessActivities.html">FitnessActivities</a></code>, encoded as an integer for efficiency. The
activity value should be stored using <code><a href="/reference/com/google/android/gms/fitness/data/Value.html#setActivity(java.lang.String)">setActivity(String)</a></code>,
and read using <code><a href="/reference/com/google/android/gms/fitness/data/Value.html#asActivity()">asActivity()</a></code>
</p></div>
-
+
</div>
</div>
@@ -3438,32 +3439,32 @@
<A NAME="FIELD_ALTITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ALTITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An altitude of a location represented as a float, in meters above sea level.
Some location samples don't have an altitude value so this field might not be set.
</p></div>
-
+
</div>
</div>
@@ -3471,31 +3472,31 @@
<A NAME="FIELD_AVERAGE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_AVERAGE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An average value.
</p></div>
-
+
</div>
</div>
@@ -3503,31 +3504,31 @@
<A NAME="FIELD_BPM"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_BPM
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A heart rate in beats per minute.
</p></div>
-
+
</div>
</div>
@@ -3535,31 +3536,31 @@
<A NAME="FIELD_CALORIES"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_CALORIES
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Calories in kcal.
</p></div>
-
+
</div>
</div>
@@ -3599,31 +3600,31 @@
<A NAME="FIELD_CONFIDENCE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_CONFIDENCE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>The confidence of an accompanied value, specified as a value between 0.0 and 100.0.
</p></div>
-
+
</div>
</div>
@@ -3631,31 +3632,31 @@
<A NAME="FIELD_DISTANCE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_DISTANCE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A distance in meters.
</p></div>
-
+
</div>
</div>
@@ -3663,31 +3664,31 @@
<A NAME="FIELD_DURATION"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_DURATION
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A duration in milliseconds.
</p></div>
-
+
</div>
</div>
@@ -3727,31 +3728,31 @@
<A NAME="FIELD_HEIGHT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HEIGHT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A height in meters.
</p></div>
-
+
</div>
</div>
@@ -3759,31 +3760,31 @@
<A NAME="FIELD_HIGH_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HIGH_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A high latitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3791,31 +3792,31 @@
<A NAME="FIELD_HIGH_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HIGH_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A high longitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3823,31 +3824,31 @@
<A NAME="FIELD_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A latitude of a location represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3855,31 +3856,31 @@
<A NAME="FIELD_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A longitude of a location represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3887,31 +3888,31 @@
<A NAME="FIELD_LOW_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LOW_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A low latitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3919,31 +3920,31 @@
<A NAME="FIELD_LOW_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LOW_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A low longitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3951,31 +3952,31 @@
<A NAME="FIELD_MAX"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_MAX
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A maximum value.
</p></div>
-
+
</div>
</div>
@@ -4015,31 +4016,31 @@
<A NAME="FIELD_MIN"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_MIN
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A minimum value.
</p></div>
-
+
</div>
</div>
@@ -4113,31 +4114,31 @@
<A NAME="FIELD_PERCENTAGE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_PERCENTAGE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A percentage value, between 0 and 100.
</p></div>
-
+
</div>
</div>
@@ -4145,31 +4146,31 @@
<A NAME="FIELD_REVOLUTIONS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_REVOLUTIONS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A count of revolutions.
</p></div>
-
+
</div>
</div>
@@ -4177,31 +4178,31 @@
<A NAME="FIELD_RPM"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_RPM
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Revolutions per minute or rate per minute.
</p></div>
-
+
</div>
</div>
@@ -4209,31 +4210,31 @@
<A NAME="FIELD_SPEED"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_SPEED
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A speed in meter/sec.
</p></div>
-
+
</div>
</div>
@@ -4241,31 +4242,31 @@
<A NAME="FIELD_STEPS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_STEPS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A count of steps.
</p></div>
-
+
</div>
</div>
@@ -4273,31 +4274,31 @@
<A NAME="FIELD_WATTS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_WATTS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Power in watts.
</p></div>
-
+
</div>
</div>
@@ -4305,31 +4306,31 @@
<A NAME="FIELD_WEIGHT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_WEIGHT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A weight in kilograms.
</p></div>
-
+
</div>
</div>
@@ -4354,14 +4355,14 @@
<A NAME="describeContents()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">describeContents</span>
@@ -4369,15 +4370,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4386,14 +4387,14 @@
<A NAME="equals(java.lang.Object)"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
boolean
</span>
<span class="sympad">equals</span>
@@ -4401,15 +4402,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4418,14 +4419,14 @@
<A NAME="getFormat()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">getFormat</span>
@@ -4433,15 +4434,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Returns the format of the field, as one of the format constant values.
</p></div>
@@ -4451,14 +4452,14 @@
<A NAME="getName()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
String
</span>
<span class="sympad">getName</span>
@@ -4466,15 +4467,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Returns the name of the field.
</p></div>
@@ -4484,14 +4485,14 @@
<A NAME="hashCode()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">hashCode</span>
@@ -4499,15 +4500,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4516,14 +4517,14 @@
<A NAME="toString()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
String
</span>
<span class="sympad">toString</span>
@@ -4531,15 +4532,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4548,14 +4549,14 @@
<A NAME="writeToParcel(android.os.Parcel, int)"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
void
</span>
<span class="sympad">writeToParcel</span>
@@ -4563,15 +4564,15 @@
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4589,17 +4590,17 @@
<A NAME="navbar_top"></A>
<div id="footer" class="wrap" >
-
+
<div id="copyright">
-
+
Except as noted, this content is licensed under <a
- href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
+ href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
For details and restrictions, see the <a href="/license.html">
Content License</a>.
</div>
<div id="build_info">
-
+
<script src="/timestamp.js" type="text/javascript"></script>
<script>document.write(BUILD_TIMESTAMP)</script>
@@ -4607,7 +4608,7 @@
<div id="footerlinks">
-
+
<p>
<a href="/about/index.html">About Android</a> |
<a href="/legal.html">Legal</a> |
@@ -4620,7 +4621,7 @@
</div><!-- end doc-content -->
-</div> <!-- end body-content -->
+</div> <!-- end body-content -->
diff --git a/docs/html/training/enterprise/app-restrictions.jd b/docs/html/training/enterprise/app-restrictions.jd
index fc5dfcc..dd2c2c0 100644
--- a/docs/html/training/enterprise/app-restrictions.jd
+++ b/docs/html/training/enterprise/app-restrictions.jd
@@ -316,8 +316,18 @@
{@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. Your app has to listen for
this intent so you can change the app's behavior when the restriction settings
- change. The following code shows how to dynamically register a broadcast
- receiver for this intent:
+ change.</p>
+
+<p class="note">
+ <strong>Note:</strong> The {@link
+ android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
+ ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent is sent only to listeners
+ that are dynamically registered, <em>not</em> to listeners that are declared
+ in the app manifest.
+</p>
+<p>
+ The following code shows how to dynamically register a broadcast receiver for
+ this intent:
</p>
<pre>IntentFilter restrictionsFilter =
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 649d996..6385706 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -185,7 +185,9 @@
public static final int VERTICAL_TEXT_FLAG = 0x1000;
// we use this when we first create a paint
- static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
+ private static final int HIDDEN_DEFAULT_PAINT_FLAGS =
+ DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;
+ private static final int DEFAULT_PAINT_FLAGS = ANTI_ALIAS_FLAG;
/**
* Font hinter option that disables font hinting.
@@ -415,9 +417,11 @@
/**
* Create a new paint with default settings.
+ *
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, sets {@link #ANTI_ALIAS_FLAG}.
*/
public Paint() {
- this(0);
+ this(DEFAULT_PAINT_FLAGS);
}
/**
@@ -428,7 +432,7 @@
*/
public Paint(int flags) {
mNativePaint = native_init();
- setFlags(flags | DEFAULT_PAINT_FLAGS);
+ setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
// setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
@@ -452,7 +456,7 @@
/** Restores the paint to its default settings. */
public void reset() {
native_reset(mNativePaint);
- setFlags(DEFAULT_PAINT_FLAGS);
+ setFlags(DEFAULT_PAINT_FLAGS | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
@@ -1870,7 +1874,7 @@
* Convenience overload that takes a char array instead of a
* String.
*
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+ * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
* @hide
*/
public float getTextRunAdvances(char[] chars, int index, int count,
@@ -1915,7 +1919,7 @@
* Convenience overload that takes a CharSequence instead of a
* String.
*
- * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+ * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
* @hide
*/
public float getTextRunAdvances(CharSequence text, int start, int end,
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 8bbfb51..513e4c8 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,6 +20,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
+import java.util.Collection;
import java.util.Iterator;
import android.annotation.IntDef;
@@ -32,6 +33,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
/**
@@ -113,6 +115,11 @@
*/
private static final int NATIVE_EVENT_NEW_POS = 3;
+ /**
+ * Event id denotes when the routing changes.
+ */
+ private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
private final static String TAG = "android.media.AudioRecord";
/** @hide */
@@ -1127,7 +1134,7 @@
* Sets the listener the AudioRecord notifies when a previously set marker is reached or
* for each periodic record head position update.
* Use this method to receive AudioRecord events in the Handler associated with another
- * thread than the one in which you created the AudioTrack instance.
+ * thread than the one in which you created the AudioRecord instance.
* @param listener
* @param handler the Handler that will receive the event notification messages.
*/
@@ -1168,6 +1175,115 @@
}
+ //--------------------------------------------------------------------------
+ // (Re)Routing Info
+ //--------------------
+ /**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
+ */
+ public AudioDeviceInfo getRoutedDevice() {
+ return null;
+ }
+
+ /**
+ * The message sent to apps when the routing of this AudioRecord changes if they provide
+ * a {#link Handler} object to addOnAudioRecordRoutingListener().
+ */
+ private ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>
+ mRoutingChangeListeners =
+ new ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link OnAudioRecordRoutingListener} to receive notifications of routing changes
+ * on this AudioRecord.
+ */
+ public void addOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mRoutingChangeListeners) {
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link OnAudioRecordRoutingListener} which has been previously added
+ * to receive notifications of changes to the set of connected audio devices.
+ */
+ public void removeOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeRoutingEventHandlerDelegate(final AudioRecord record,
+ final OnAudioRecordRoutingListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioRecord was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (record == null) {
+ return;
+ }
+ switch(msg.what) {
+ case NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onAudioRecordRouting(record);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
+ /**
+ * Sends device list change notification to all listeners.
+ */
+ private void broadcastRoutingChange() {
+ Collection<NativeRoutingEventHandlerDelegate> values;
+ synchronized (mRoutingChangeListeners) {
+ values = mRoutingChangeListeners.values();
+ }
+ for(NativeRoutingEventHandlerDelegate delegate : values) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
+ }
+
/**
* Sets the period at which the listener is called, if set with
* {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or
@@ -1184,6 +1300,39 @@
}
+ //--------------------------------------------------------------------------
+ // Explicit Routing
+ //--------------------
+ private AudioDeviceInfo mPreferredDevice = null;
+
+ /**
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
+ * the input to this AudioRecord.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source.
+ * If deviceInfo is null, default routing is restored.
+ * @return true if successful, false if the specified {@link AudioDeviceInfo} is non-null and
+ * does not correspond to a valid audio input device.
+ */
+ public boolean setPreferredInputDevice(AudioDeviceInfo deviceInfo) {
+ // Do some validation....
+ if (deviceInfo != null && !deviceInfo.isSource()) {
+ return false;
+ }
+
+ mPreferredDevice = deviceInfo;
+ int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
+
+ return native_setInputDevice(preferredDeviceId);
+ }
+
+ /**
+ * Returns the selected input specified by {@link #setPreferredInputDevice}. Note that this
+ * is not guarenteed to correspond to the actual device being used for recording.
+ */
+ public AudioDeviceInfo getPreferredInputDevice() {
+ return mPreferredDevice;
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -1314,6 +1463,8 @@
static private native final int native_get_min_buff_size(
int sampleRateInHz, int channelCount, int audioFormat);
+ private native final boolean native_setInputDevice(int deviceId);
+
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 093ff26..6785ebc 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,6 +23,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
+import java.util.Collection;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -37,6 +38,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.IAppOpsService;
@@ -176,6 +178,12 @@
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
+ /**
+ * Event id denotes when the routing changes.
+ */
+ private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
+
private final static String TAG = "android.media.AudioTrack";
@@ -224,7 +232,7 @@
/**
* Handler for events coming from the native code.
*/
- private NativeEventHandlerDelegate mEventHandlerDelegate;
+ private NativePositionEventHandlerDelegate mEventHandlerDelegate;
/**
* Looper associated with the thread that creates the AudioTrack instance.
*/
@@ -1243,7 +1251,7 @@
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
Handler handler) {
if (listener != null) {
- mEventHandlerDelegate = new NativeEventHandlerDelegate(this, listener, handler);
+ mEventHandlerDelegate = new NativePositionEventHandlerDelegate(this, listener, handler);
} else {
mEventHandlerDelegate = null;
}
@@ -2082,33 +2090,93 @@
private AudioDeviceInfo mPreferredDevice = null;
/**
- * Specifies an audio device (via and {@link AudioDeviceInfo} object) to route
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
* the output from this AudioTrack.
- * @param deviceSpec The {@link AudioDeviceInfo} specifying the physical audio device.
- * If deviceSpec is null, default routing is restored.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink.
+ * If deviceInfo is null, default routing is restored.
* @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio output device.
*/
- public boolean setPreferredOutputDevice(AudioDeviceInfo deviceSpec) {
+ public boolean setPreferredOutputDevice(AudioDeviceInfo deviceInfo) {
// Do some validation....
- if (deviceSpec != null && !deviceSpec.isSink()) {
+ if (deviceInfo != null && !deviceInfo.isSink()) {
return false;
}
- mPreferredDevice = deviceSpec;
- int routingDeviceId = mPreferredDevice != null ? deviceSpec.getId() : 0;
+ mPreferredDevice = deviceInfo;
+ int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
- return native_setOutputDevice(routingDeviceId);
+ return native_setOutputDevice(preferredDeviceId);
}
/**
* Returns the selected output specified by {@link #setPreferredOutputDevice}. Note that this
- * is not guarenteed to correspond to the actual device being used for playback.
+ * is not guaranteed to correspond to the actual device being used for playback.
*/
public AudioDeviceInfo getPreferredOutputDevice() {
return mPreferredDevice;
}
+ //--------------------------------------------------------------------------
+ // (Re)Routing Info
+ //--------------------
+ /**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
+ */
+ public AudioDeviceInfo getRoutedDevice() {
+ return null;
+ }
+
+ /**
+ * The message sent to apps when the routing of this AudioTrack changes if they provide
+ * a {#link Handler} object to addOnAudioTrackRoutingListener().
+ */
+ private ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>
+ mRoutingChangeListeners =
+ new ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link OnAudioTrackRoutingListener} to receive notifications of routing changes
+ * on this AudioTrack.
+ */
+ public void addOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mRoutingChangeListeners) {
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link OnAudioTrackRoutingListener} which has been previously added
+ * to receive notifications of changes to the set of connected audio devices.
+ */
+ public void removeOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Sends device list change notification to all listeners.
+ */
+ private void broadcastRoutingChange() {
+ Collection<NativeRoutingEventHandlerDelegate> values;
+ synchronized (mRoutingChangeListeners) {
+ values = mRoutingChangeListeners.values();
+ }
+ for(NativeRoutingEventHandlerDelegate delegate : values) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -2137,10 +2205,10 @@
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
- private class NativeEventHandlerDelegate {
+ private class NativePositionEventHandlerDelegate {
private final Handler mHandler;
- NativeEventHandlerDelegate(final AudioTrack track,
+ NativePositionEventHandlerDelegate(final AudioTrack track,
final OnPlaybackPositionUpdateListener listener,
Handler handler) {
// find the looper for our new event handler
@@ -2188,6 +2256,55 @@
}
}
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeRoutingEventHandlerDelegate(final AudioTrack track,
+ final OnAudioTrackRoutingListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioTrack was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (track == null) {
+ return;
+ }
+ switch(msg.what) {
+ case NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onAudioTrackRouting(track);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
//---------------------------------------------------------
// Java methods called from the native side
@@ -2201,7 +2318,7 @@
return;
}
- NativeEventHandlerDelegate delegate = track.mEventHandlerDelegate;
+ NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
if (handler != null) {
diff --git a/media/java/android/media/OnAudioRecordRoutingListener.java b/media/java/android/media/OnAudioRecordRoutingListener.java
new file mode 100644
index 0000000..8ff41c5
--- /dev/null
+++ b/media/java/android/media/OnAudioRecordRoutingListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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.media;
+
+/**
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
+ */
+public interface OnAudioRecordRoutingListener {
+ /**
+ * Called when the routing of an AudioRecord changes from either and explicit or
+ * policy rerouting.
+ */
+ public void onAudioRecordRouting(AudioRecord audioRecord);
+}
diff --git a/media/java/android/media/OnAudioTrackRoutingListener.java b/media/java/android/media/OnAudioTrackRoutingListener.java
new file mode 100644
index 0000000..18c72ef
--- /dev/null
+++ b/media/java/android/media/OnAudioTrackRoutingListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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.media;
+
+/**
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
+ */
+public interface OnAudioTrackRoutingListener {
+ /**
+ * Called when the routing of an AudioTrack changes from either and explicit or
+ * policy rerouting.
+ */
+ public void onAudioTrackRouting(AudioTrack audioTrack);
+}
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 2f97809..67d8ab6 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -11,3 +11,5 @@
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 062d433..5281087 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -121,22 +121,20 @@
<string name="copy_remaining"><xliff:g id="duration" example="3 minutes">%s</xliff:g> left</string>
<!-- Toast shown when a file copy is kicked off -->
<plurals name="copy_begin">
- <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
- <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
+ <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
+ <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
</plurals>
<!-- Text shown on the copy notification while DocumentsUI performs setup in preparation for copying files [CHAR LIMIT=32] -->
<string name="copy_preparing">Preparing for copy\u2026</string>
<!-- Title of the copy error notification [CHAR LIMIT=48] -->
<plurals name="copy_error_notification_title">
- <item quantity="one">Error copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
- <item quantity="other">Error copying <xliff:g id="count" example="1">%1$d</xliff:g> files.</item>
+ <item quantity="one">Couldn\'t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
+ <item quantity="other">Couldn\'t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
</plurals>
<!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] -->
<string name="notification_touch_for_details">Touch to view details</string>
<!-- Label of a dialog button for retrying a failed operation [CHAR LIMIT=24] -->
<string name="retry">Retry</string>
- <!-- Title of the copying failure alert dialog. [CHAR LIMIT=48] -->
- <string name="copy_failure_alert_title">Error copying files</string>
<!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] -->
- <string name="copy_failure_alert_content">Following files are not copied: <xliff:g id="list">%1$s</xliff:g></string>
+ <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index a9f03b6..2e0bece 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -26,6 +26,7 @@
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
@@ -37,12 +38,14 @@
import android.provider.DocumentsContract.Document;
import android.text.format.DateUtils;
import android.util.Log;
+import android.widget.Toast;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import libcore.io.IoUtils;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -70,7 +73,7 @@
private volatile boolean mIsCancelled;
// Parameters of the copy job. Requests to an IntentService are serialized so this code only
// needs to deal with one job at a time.
- private final ArrayList<Uri> mFailedFiles;
+ private final ArrayList<DocumentInfo> mFailedFiles;
private long mBatchSize;
private long mBytesCopied;
private long mStartTime;
@@ -88,7 +91,27 @@
public CopyService() {
super("CopyService");
- mFailedFiles = new ArrayList<Uri>();
+ mFailedFiles = new ArrayList<DocumentInfo>();
+ }
+
+ /**
+ * Starts the service for a copy operation.
+ *
+ * @param context Context for the intent.
+ * @param srcDocs A list of src files to copy.
+ * @param dstStack The copy destination stack.
+ */
+ public static void start(Context context, List<DocumentInfo> srcDocs, DocumentStack dstStack) {
+ final Resources res = context.getResources();
+ final Intent copyIntent = new Intent(context, CopyService.class);
+ copyIntent.putParcelableArrayListExtra(
+ EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
+ copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+
+ Toast.makeText(context,
+ res.getQuantityString(R.plurals.copy_begin, srcDocs.size(), srcDocs.size()),
+ Toast.LENGTH_SHORT).show();
+ context.startService(copyIntent);
}
@Override
@@ -360,7 +383,7 @@
if (dstUri == null) {
// If this is a directory, the entire subdir will not be copied over.
Log.e(TAG, "Error while copying " + srcInfo.displayName);
- mFailedFiles.add(srcInfo.derivedUri);
+ mFailedFiles.add(srcInfo);
return;
}
@@ -444,7 +467,12 @@
} catch (IOException e) {
errorOccurred = true;
Log.e(TAG, "Error while copying " + srcUri.toString(), e);
- mFailedFiles.add(srcUri);
+ try {
+ mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
+ } catch (FileNotFoundException ignore) {
+ Log.w(TAG, "Source file gone: " + srcUri, e);
+ // The source file is gone.
+ }
} finally {
// This also ensures the file descriptors are closed.
IoUtils.closeQuietly(src);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 7cf58cc..a789da8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -82,6 +82,7 @@
import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
import com.google.android.collect.Lists;
@@ -341,9 +342,6 @@
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- final Context context = getActivity();
- final Resources res = context.getResources();
-
// There's only one request code right now. Replace this with a switch statement or
// something more scalable when more codes are added.
if (requestCode != REQUEST_COPY_DESTINATION) {
@@ -355,15 +353,8 @@
return;
}
- final List<DocumentInfo> docs = getDisplayState(this).selectedDocumentsForCopy;
- final Intent copyIntent = new Intent(context, CopyService.class);
- copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs));
- copyIntent.putExtra(CopyService.EXTRA_STACK, data.getParcelableExtra(CopyService.EXTRA_STACK));
-
- Toast.makeText(context,
- res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()),
- Toast.LENGTH_SHORT).show();
- context.startService(copyIntent);
+ CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
+ (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK));
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 1748c9c..00b0f78 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.DialogInterface;
@@ -27,7 +28,9 @@
import android.os.Bundle;
import android.text.Html;
+import com.android.documentsui.CopyService;
import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -40,9 +43,10 @@
private static final String TAG = "FailureDialogFragment";
private int mFailure;
- private ArrayList<Uri> mFailedSrcList;
+ private ArrayList<DocumentInfo> mFailedSrcList;
- public static void show(FragmentManager fm, int failure, ArrayList<Uri> failedSrcList) {
+ public static void show(FragmentManager fm, int failure,
+ ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack) {
// TODO: Add support for other failures than copy.
if (failure != CopyService.FAILURE_COPY) {
return;
@@ -62,7 +66,11 @@
@Override
public void onClick(DialogInterface dialog, int whichButton) {
- // TODO: Pass mFailure and mFailedSrcList to the parent fragment.
+ if (whichButton == DialogInterface.BUTTON_POSITIVE) {
+ CopyService.start(getActivity(), mFailedSrcList,
+ (DocumentStack) getActivity().getIntent().getParcelableExtra(
+ CopyService.EXTRA_STACK));
+ }
}
@Override
@@ -73,27 +81,17 @@
mFailedSrcList = getArguments().getParcelableArrayList(CopyService.EXTRA_SRC_LIST);
final StringBuilder list = new StringBuilder("<p>");
- for (Uri documentUri : mFailedSrcList) {
- try {
- final DocumentInfo documentInfo = DocumentInfo.fromUri(
- getActivity().getContentResolver(), documentUri);
- list.append(String.format("• %s<br>", documentInfo.displayName));
- }
- catch (FileNotFoundException ignore) {
- // Source file most probably gone.
- }
+ for (DocumentInfo documentInfo : mFailedSrcList) {
+ list.append(String.format("• %s<br>", documentInfo.displayName));
}
list.append("</p>");
final String message = String.format(getString(R.string.copy_failure_alert_content),
list.toString());
return new AlertDialog.Builder(getActivity())
- .setTitle(getString(R.string.copy_failure_alert_title))
.setMessage(Html.fromHtml(message))
- // TODO: Implement retrying the copy operation.
.setPositiveButton(R.string.retry, this)
.setNegativeButton(android.R.string.cancel, this)
- .setIcon(android.R.drawable.ic_dialog_alert)
.create();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index 1d021cb..cad277d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -106,12 +106,15 @@
RootsFragment.show(getFragmentManager(), null);
if (!mState.restored) {
new RestoreStackTask().execute();
+
+ // Show a failure dialog if there was a failed operation.
final Intent intent = getIntent();
+ final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
if (failure != 0) {
- final ArrayList<Uri> failedSrcList = intent.getParcelableArrayListExtra(
- CopyService.EXTRA_SRC_LIST);
- FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList);
+ final ArrayList<DocumentInfo> failedSrcList =
+ intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
+ FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack);
}
} else {
onCurrentDirectoryChanged(ANIM_NONE);
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index aff57bf..73a723d 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -45,7 +45,6 @@
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
@@ -55,7 +54,6 @@
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
-import java.util.Objects;
public class ExternalStorageProvider extends DocumentsProvider {
private static final String TAG = "ExternalStorage";
@@ -327,7 +325,7 @@
throw new IllegalArgumentException("Parent document isn't a directory");
}
- final File file = buildUniqueFile(parent, mimeType, displayName);
+ final File file = FileUtils.buildUniqueFile(parent, mimeType, displayName);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
if (!file.mkdir()) {
throw new IllegalStateException("Failed to mkdir " + file);
@@ -345,68 +343,6 @@
return getDocIdForFile(file);
}
- private static File buildFile(File parent, String name, String ext) {
- if (TextUtils.isEmpty(ext)) {
- return new File(parent, name);
- } else {
- return new File(parent, name + "." + ext);
- }
- }
-
- @VisibleForTesting
- public static File buildUniqueFile(File parent, String mimeType, String displayName)
- throws FileNotFoundException {
- String name;
- String ext;
-
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- name = displayName;
- ext = null;
- } else {
- String mimeTypeFromExt;
-
- // Extract requested extension from display name
- final int lastDot = displayName.lastIndexOf('.');
- if (lastDot >= 0) {
- name = displayName.substring(0, lastDot);
- ext = displayName.substring(lastDot + 1);
- mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- ext.toLowerCase());
- } else {
- name = displayName;
- ext = null;
- mimeTypeFromExt = null;
- }
-
- if (mimeTypeFromExt == null) {
- mimeTypeFromExt = "application/octet-stream";
- }
-
- final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- mimeType);
- if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
- // Extension maps back to requested MIME type; allow it
- } else {
- // No match; insist that create file matches requested MIME
- name = displayName;
- ext = extFromMimeType;
- }
- }
-
- File file = buildFile(parent, name, ext);
-
- // If conflicting file, try adding counter suffix
- int n = 0;
- while (file.exists()) {
- if (n++ >= 32) {
- throw new FileNotFoundException("Failed to create unique file");
- }
- file = buildFile(parent, name + " (" + n + ")", ext);
- }
-
- return file;
- }
-
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
// Since this provider treats renames as generating a completely new
diff --git a/packages/ExternalStorageProvider/tests/Android.mk b/packages/ExternalStorageProvider/tests/Android.mk
deleted file mode 100644
index 830731a..0000000
--- a/packages/ExternalStorageProvider/tests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_PACKAGE_NAME := ExternalStorageProviderTests
-LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
deleted file mode 100644
index ffcd499..0000000
--- a/packages/ExternalStorageProvider/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.externalstorage.tests">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.externalstorage"
- android:label="Tests for ExternalStorageProvider" />
-
-</manifest>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
deleted file mode 100644
index f980b60..0000000
--- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You 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.externalstorage;
-
-import static com.android.externalstorage.ExternalStorageProvider.buildUniqueFile;
-
-import android.os.FileUtils;
-import android.provider.DocumentsContract.Document;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import java.io.File;
-
-@MediumTest
-public class ExternalStorageProviderTest extends AndroidTestCase {
-
- private File mTarget;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mTarget = getContext().getFilesDir();
- FileUtils.deleteContents(mTarget);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- FileUtils.deleteContents(mTarget);
- }
-
- public void testBuildUniqueFile_normal() throws Exception {
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test"));
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- assertNameEquals("test.jpeg", buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
- assertNameEquals("TEst.JPeg", buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
- assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
- assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png"));
-
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test.flac"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
- }
-
- public void testBuildUniqueFile_unknown() throws Exception {
- assertNameEquals("test", buildUniqueFile(mTarget, "application/octet-stream", "test"));
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
- assertNameEquals(".test", buildUniqueFile(mTarget, "application/octet-stream", ".test"));
-
- assertNameEquals("test", buildUniqueFile(mTarget, "lolz/lolz", "test"));
- assertNameEquals("test.lolz", buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
- }
-
- public void testBuildUniqueFile_dir() throws Exception {
- assertNameEquals("test", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
- new File(mTarget, "test").mkdir();
- assertNameEquals("test (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
-
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
- new File(mTarget, "test.jpg").mkdir();
- assertNameEquals("test.jpg (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
- }
-
- public void testBuildUniqueFile_increment() throws Exception {
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- new File(mTarget, "test.jpg").createNewFile();
- assertNameEquals("test (1).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- new File(mTarget, "test (1).jpg").createNewFile();
- assertNameEquals("test (2).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- }
-
- private static void assertNameEquals(String expected, File actual) {
- assertEquals(expected, actual.getName());
- }
-}
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index 4fbcc1e..e083c9c 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -18,6 +18,7 @@
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import android.content.Context;
import android.content.Intent;
@@ -141,7 +142,11 @@
plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
}
if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
- text = concatenate(plmn, spn);
+ if (Objects.equals(plmn, spn)) {
+ text = plmn;
+ } else {
+ text = concatenate(plmn, spn);
+ }
}
displayText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.keyguard_missing_sim_message_short), text);
@@ -293,11 +298,7 @@
final boolean plmnValid = !TextUtils.isEmpty(plmn);
final boolean spnValid = !TextUtils.isEmpty(spn);
if (plmnValid && spnValid) {
- if (plmn.equals(spn)) {
- return plmn;
- } else {
- return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
- }
+ return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
} else if (plmnValid) {
return plmn;
} else if (spnValid) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
new file mode 100644
index 0000000..e0af29d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2011 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.settingslib.deviceinfo;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
+import android.util.Log;
+import android.util.SparseLongArray;
+
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.ArrayUtils;
+import com.google.android.collect.Sets;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility for measuring the disk usage of internal storage or a physical
+ * {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
+ * and delivers results to {@link MeasurementReceiver}.
+ */
+public class StorageMeasurement {
+ private static final String TAG = "StorageMeasurement";
+
+ private static final boolean LOCAL_LOGV = true;
+ static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
+
+ public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
+
+ /** Media types to measure on external storage. */
+ private static final Set<String> sMeasureMediaTypes = Sets.newHashSet(
+ Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
+ Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
+ Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
+
+ public static class MeasurementDetails {
+ /**
+ * Total apps disk usage.
+ * <p>
+ * When measuring internal storage, this value includes the code size of
+ * all apps (regardless of install status for current user), and
+ * internal disk used by the current user's apps. When the device
+ * emulates external storage, this value also includes emulated storage
+ * used by the current user's apps.
+ * <p>
+ * When measuring a physical {@link StorageVolume}, this value includes
+ * usage by all apps on that volume.
+ */
+ public long appsSize;
+
+ /**
+ * Total cache disk usage by apps.
+ */
+ public long cacheSize;
+
+ /**
+ * Total media disk usage, categorized by types such as
+ * {@link Environment#DIRECTORY_MUSIC}.
+ * <p>
+ * When measuring internal storage, this reflects media on emulated
+ * storage for the current user.
+ * <p>
+ * When measuring a physical {@link StorageVolume}, this reflects media
+ * on that volume.
+ */
+ public HashMap<String, Long> mediaSize = new HashMap<>();
+
+ /**
+ * Misc external disk usage for the current user, unaccounted in
+ * {@link #mediaSize}.
+ */
+ public long miscSize;
+
+ /**
+ * Total disk usage for users, which is only meaningful for emulated
+ * internal storage. Key is {@link UserHandle}.
+ */
+ public SparseLongArray usersSize = new SparseLongArray();
+ }
+
+ public interface MeasurementReceiver {
+ public void onDetailsChanged(MeasurementDetails details);
+ }
+
+ private WeakReference<MeasurementReceiver> mReceiver;
+
+ private final Context mContext;
+
+ private final VolumeInfo mVolume;
+ private final VolumeInfo mSharedVolume;
+
+ private final MainHandler mMainHandler;
+ private final MeasurementHandler mMeasurementHandler;
+
+ public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
+ mContext = context.getApplicationContext();
+
+ mVolume = volume;
+ mSharedVolume = sharedVolume;
+
+ // Start the thread that will measure the disk usage.
+ final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
+ handlerThread.start();
+
+ mMainHandler = new MainHandler();
+ mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
+ }
+
+ public void setReceiver(MeasurementReceiver receiver) {
+ if (mReceiver == null || mReceiver.get() == null) {
+ mReceiver = new WeakReference<MeasurementReceiver>(receiver);
+ }
+ }
+
+ public void forceMeasure() {
+ invalidate();
+ measure();
+ }
+
+ public void measure() {
+ if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
+ }
+ }
+
+ public void onDestroy() {
+ mReceiver = null;
+ mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
+ }
+
+ private void invalidate() {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
+ }
+
+ private static class StatsObserver extends IPackageStatsObserver.Stub {
+ private final boolean mIsPrivate;
+ private final MeasurementDetails mDetails;
+ private final int mCurrentUser;
+ private final Message mFinished;
+
+ private int mRemaining;
+
+ public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
+ Message finished, int remaining) {
+ mIsPrivate = isPrivate;
+ mDetails = details;
+ mCurrentUser = currentUser;
+ mFinished = finished;
+ mRemaining = remaining;
+ }
+
+ @Override
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ synchronized (mDetails) {
+ if (succeeded) {
+ addStatsLocked(stats);
+ }
+ if (--mRemaining == 0) {
+ mFinished.sendToTarget();
+ }
+ }
+ }
+
+ private void addStatsLocked(PackageStats stats) {
+ if (mIsPrivate) {
+ long codeSize = stats.codeSize;
+ long dataSize = stats.dataSize;
+ long cacheSize = stats.cacheSize;
+ if (Environment.isExternalStorageEmulated()) {
+ // Include emulated storage when measuring internal. OBB is
+ // shared on emulated storage, so treat as code.
+ codeSize += stats.externalCodeSize + stats.externalObbSize;
+ dataSize += stats.externalDataSize + stats.externalMediaSize;
+ cacheSize += stats.externalCacheSize;
+ }
+
+ // Count code and data for current user
+ if (stats.userHandle == mCurrentUser) {
+ mDetails.appsSize += codeSize;
+ mDetails.appsSize += dataSize;
+ }
+
+ // User summary only includes data (code is only counted once
+ // for the current user)
+ addValue(mDetails.usersSize, stats.userHandle, dataSize);
+
+ // Include cache for all users
+ mDetails.cacheSize += cacheSize;
+
+ } else {
+ // Physical storage; only count external sizes
+ mDetails.appsSize += stats.externalCodeSize + stats.externalDataSize
+ + stats.externalMediaSize + stats.externalObbSize;
+ mDetails.cacheSize += stats.externalCacheSize;
+ }
+ }
+ }
+
+ private class MainHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ final MeasurementDetails details = (MeasurementDetails) msg.obj;
+ final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
+ if (receiver != null) {
+ receiver.onDetailsChanged(details);
+ }
+ }
+ }
+
+ private class MeasurementHandler extends Handler {
+ public static final int MSG_MEASURE = 1;
+ public static final int MSG_CONNECTED = 2;
+ public static final int MSG_DISCONNECT = 3;
+ public static final int MSG_COMPLETED = 4;
+ public static final int MSG_INVALIDATE = 5;
+
+ private Object mLock = new Object();
+
+ private IMediaContainerService mDefaultContainer;
+
+ private volatile boolean mBound = false;
+
+ private MeasurementDetails mCached;
+
+ private final ServiceConnection mDefContainerConn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
+ service);
+ mDefaultContainer = imcs;
+ mBound = true;
+ sendMessage(obtainMessage(MSG_CONNECTED, imcs));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBound = false;
+ removeMessages(MSG_CONNECTED);
+ }
+ };
+
+ public MeasurementHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_MEASURE: {
+ if (mCached != null) {
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
+ break;
+ }
+
+ synchronized (mLock) {
+ if (mBound) {
+ removeMessages(MSG_DISCONNECT);
+ sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
+ } else {
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ mContext.bindServiceAsUser(service, mDefContainerConn,
+ Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ }
+ }
+ break;
+ }
+ case MSG_CONNECTED: {
+ final IMediaContainerService imcs = (IMediaContainerService) msg.obj;
+ measureExactStorage(imcs);
+ break;
+ }
+ case MSG_DISCONNECT: {
+ synchronized (mLock) {
+ if (mBound) {
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
+ }
+ break;
+ }
+ case MSG_COMPLETED: {
+ mCached = (MeasurementDetails) msg.obj;
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
+ break;
+ }
+ case MSG_INVALIDATE: {
+ mCached = null;
+ break;
+ }
+ }
+ }
+ }
+
+ private void measureExactStorage(IMediaContainerService imcs) {
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ final PackageManager packageManager = mContext.getPackageManager();
+
+ final List<UserInfo> users = userManager.getUsers();
+ final int currentUser = ActivityManager.getCurrentUser();
+
+ final MeasurementDetails details = new MeasurementDetails();
+ final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
+ details);
+
+ if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
+ final File basePath = mSharedVolume.getPathForUser(currentUser);
+
+ // Measure media types for emulated storage, or for primary physical
+ // external volume
+ for (String type : sMeasureMediaTypes) {
+ final File path = new File(basePath, type);
+ final long size = getDirectorySize(imcs, path);
+ details.mediaSize.put(type, size);
+ }
+
+ // Measure misc files not counted under media
+ details.miscSize = measureMisc(imcs, basePath);
+
+ if (mSharedVolume.getType() == VolumeInfo.TYPE_EMULATED) {
+ // Measure total emulated storage of all users; internal apps data
+ // will be spliced in later
+ for (UserInfo user : users) {
+ final File userPath = mSharedVolume.getPathForUser(user.id);
+ final long size = getDirectorySize(imcs, userPath);
+ addValue(details.usersSize, user.id, size);
+ }
+ }
+ }
+
+ // Measure all apps hosted on this volume for all users
+ if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
+ final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+
+ final List<ApplicationInfo> volumeApps = new ArrayList<>();
+ for (ApplicationInfo app : apps) {
+ if (Objects.equals(app.volumeUuid, mVolume.getFsUuid())) {
+ volumeApps.add(app);
+ }
+ }
+
+ final int count = users.size() * volumeApps.size();
+ if (count == 0) {
+ finished.sendToTarget();
+ return;
+ }
+
+ final StatsObserver observer = new StatsObserver(
+ true, details, currentUser, finished, count);
+ for (UserInfo user : users) {
+ for (ApplicationInfo app : volumeApps) {
+ packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
+ }
+ }
+
+ } else {
+ finished.sendToTarget();
+ return;
+ }
+ }
+
+ private static long getDirectorySize(IMediaContainerService imcs, File path) {
+ try {
+ final long size = imcs.calculateDirectorySize(path.toString());
+ Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
+ return size;
+ } catch (Exception e) {
+ Log.w(TAG, "Could not read memory from default container service for " + path, e);
+ return 0;
+ }
+ }
+
+ private long measureMisc(IMediaContainerService imcs, File dir) {
+ final File[] files = dir.listFiles();
+ if (ArrayUtils.isEmpty(files)) return 0;
+
+ // Get sizes of all top level nodes except the ones already computed
+ long miscSize = 0;
+ for (File file : files) {
+ final String name = file.getName();
+ if (sMeasureMediaTypes.contains(name)) {
+ continue;
+ }
+
+ if (file.isFile()) {
+ miscSize += file.length();
+ } else if (file.isDirectory()) {
+ miscSize += getDirectorySize(imcs, file);
+ }
+ }
+ return miscSize;
+ }
+
+ private static void addValue(SparseLongArray array, int key, long value) {
+ array.put(key, array.get(key) + value);
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 0385d1e..8d99a64 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -387,6 +387,7 @@
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
destination.failWrite(out);
+ throw new IllegalStateException("Failed to write settings, restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
@@ -408,10 +409,9 @@
parser.setInput(in, null);
parseStateLocked(parser);
- // Any error while parsing is fatal.
- } catch (Throwable t) {
+ } catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing settings file: "
- + mStatePersistFile , t);
+ + mStatePersistFile , e);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md
index f694b8c..18ae4cb 100644
--- a/packages/SystemUI/docs/demo_mode.md
+++ b/packages/SystemUI/docs/demo_mode.md
@@ -22,38 +22,39 @@
<br/>
Commands are sent as string extras with key ```command``` (required). Possible values are:
-Command | Subcommand | Argument | Description
---- | --- | --- | ---
-```enter``` | | | Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)
-```exit``` | | | Exits demo mode, bars back to their system-driven state
-```battery``` | | | Control the battery display
- | ```level``` | | Sets the battery level (0 - 100)
- | ```plugged``` | | Sets charging state (```true```, ```false```)
-```network``` | | | Control the RSSI display
- | ```airplane``` | | ```show``` to show icon, any other value to hide
- | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
- | ```wifi``` | | ```show``` to show icon, any other value to hide
- | |```level``` | Sets wifi level (null or 0-4)
- | ```mobile``` | | ```show``` to show icon, any other value to hide
- | |```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
- | |```level``` | Sets mobile signal strength level (null or 0-4)
-```bars``` | | | Control the visual style of the bars (opaque, translucent, etc)
- | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent)
-```status``` | | | Control the system status icons
- | ```volume``` | | Sets the icon in the volume slot (```silent```, ```vibrate```, any other value to hide)
- | ```bluetooth``` | | Sets the icon in the bluetooth slot (```connected```, ```disconnected```, any other value to hide)
- | ```location``` | | Sets the icon in the location slot (```show```, any other value to hide)
- | ```alarm``` | | Sets the icon in the alarm_clock slot (```show```, any other value to hide)
- | ```sync``` | | Sets the icon in the sync_active slot (```show```, any other value to hide)
- | ```tty``` | | Sets the icon in the tty slot (```show```, any other value to hide)
- | ```eri``` | | Sets the icon in the cdma_eri slot (```show```, any other value to hide)
- | ```mute``` | | Sets the icon in the mute slot (```show```, any other value to hide)
- | ```speakerphone``` | | Sets the icon in the speakerphone slot (```show```, any other value to hide)
-```notifications``` | | | Control the notification icons
- | ```visible``` | | ```false``` to hide the notification icons, any other value to show
-```clock``` | | | Control the clock display
- | ```millis``` | | Sets the time in millis
- | ```hhmm``` | | Sets the time in hh:mm
+Command | Subcommand | Argument | Description
+--- | --- | --- | ---
+```enter``` | | | Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)
+```exit``` | | | Exits demo mode, bars back to their system-driven state
+```battery``` | | | Control the battery display
+ | ```level``` | | Sets the battery level (0 - 100)
+ | ```plugged``` | | Sets charging state (```true```, ```false```)
+```network``` | | | Control the RSSI display
+ | ```airplane``` | | ```show``` to show icon, any other value to hide
+ | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
+ | ```wifi``` | | ```show``` to show icon, any other value to hide
+ | | ```level``` | Sets wifi level (null or 0-4)
+ | ```mobile``` | | ```show``` to show icon, any other value to hide
+ | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
+ | | ```level``` | Sets mobile signal strength level (null or 0-4)
+ | ```carriernetworkchange``` | | Sets mobile signal icon to carrier network change UX when disconnected (```show``` to show icon, any other value to hide)
+```bars``` | | | Control the visual style of the bars (opaque, translucent, etc)
+ | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent)
+```status``` | | | Control the system status icons
+ | ```volume``` | | Sets the icon in the volume slot (```silent```, ```vibrate```, any other value to hide)
+ | ```bluetooth``` | | Sets the icon in the bluetooth slot (```connected```, ```disconnected```, any other value to hide)
+ | ```location``` | | Sets the icon in the location slot (```show```, any other value to hide)
+ | ```alarm``` | | Sets the icon in the alarm_clock slot (```show```, any other value to hide)
+ | ```sync``` | | Sets the icon in the sync_active slot (```show```, any other value to hide)
+ | ```tty``` | | Sets the icon in the tty slot (```show```, any other value to hide)
+ | ```eri``` | | Sets the icon in the cdma_eri slot (```show```, any other value to hide)
+ | ```mute``` | | Sets the icon in the mute slot (```show```, any other value to hide)
+ | ```speakerphone``` | | Sets the icon in the speakerphone slot (```show```, any other value to hide)
+```notifications``` | | | Control the notification icons
+ | ```visible``` | | ```false``` to hide the notification icons, any other value to show
+```clock``` | | | Control the clock display
+ | ```millis``` | | Sets the time in millis
+ | ```hhmm``` | | Sets the time in hh:mm
## Examples
Enter demo mode
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ccbd0a6..6e59029 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -570,4 +570,7 @@
<!-- Padding to be used on the bottom of the fingerprint icon on Keyguard so it better aligns
with the other icons. -->
<dimen name="fingerprint_icon_additional_padding">12dp</dimen>
+
+ <!-- Minimum margin of the notification panel on the side, when being positioned dynamically -->
+ <dimen name="notification_panel_min_side_margin">48dp</dimen>
</resources>
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 03e5746..7d61099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -187,6 +187,9 @@
private boolean mExpansionIsFromHeadsUp;
private int mBottomBarHeight;
private boolean mExpandingFromHeadsUp;
+ private int mPositionMinSideMargin;
+ private int mLastOrientation = -1;
+
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
public void run() {
@@ -236,6 +239,7 @@
mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
mSecureCameraLaunchManager =
new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea);
+ mLastOrientation = getResources().getConfiguration().orientation;
// recompute internal state when qspanel height changes
mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@@ -268,6 +272,8 @@
getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
mQsFalsingThreshold = getResources().getDimensionPixelSize(
R.dimen.qs_falsing_threshold);
+ mPositionMinSideMargin = getResources().getDimensionPixelSize(
+ R.dimen.notification_panel_min_side_margin);
}
public void updateResources() {
@@ -692,6 +698,9 @@
if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
return true;
}
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
+ updateVerticalPanelPosition(event.getX());
+ }
super.onTouchEvent(event);
return true;
}
@@ -740,7 +749,7 @@
}
private boolean isInQsArea(float x, float y) {
- return (x >= mScrollView.getLeft() && x <= mScrollView.getRight()) &&
+ return (x >= mScrollView.getX() && x <= mScrollView.getX() + mScrollView.getWidth()) &&
(y <= mNotificationStackScroller.getBottomMostNotificationBottom()
|| y <= mQsContainer.getY() + mQsContainer.getHeight());
}
@@ -939,7 +948,7 @@
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
}
-
+ resetVerticalPanelPosition();
updateQsState();
}
@@ -1376,7 +1385,7 @@
return false;
}
View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
- boolean onHeader = x >= header.getLeft() && x <= header.getRight()
+ boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y);
@@ -1771,6 +1780,10 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mAfforanceHelper.onConfigurationChanged();
+ if (newConfig.orientation != mLastOrientation) {
+ resetVerticalPanelPosition();
+ }
+ mLastOrientation = newConfig.orientation;
}
@Override
@@ -2185,4 +2198,42 @@
mExpandingFromHeadsUp = true;
}
}
+
+ @Override
+ protected void onClosingFinished() {
+ super.onClosingFinished();
+ resetVerticalPanelPosition();
+ }
+
+ /**
+ * Updates the vertical position of the panel so it is positioned closer to the touch
+ * responsible for opening the panel.
+ *
+ * @param x the x-coordinate the touch event
+ */
+ private void updateVerticalPanelPosition(float x) {
+ if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
+ resetVerticalPanelPosition();
+ return;
+ }
+ float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
+ float rightMost = getWidth() - mPositionMinSideMargin
+ - mNotificationStackScroller.getWidth() / 2;
+ if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
+ x = getWidth() / 2;
+ }
+ x = Math.min(rightMost, Math.max(leftMost, x));
+ setVerticalPanelTranslation(x -
+ (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth()/2));
+ }
+
+ private void resetVerticalPanelPosition() {
+ setVerticalPanelTranslation(0f);
+ }
+
+ private void setVerticalPanelTranslation(float translation) {
+ mNotificationStackScroller.setTranslationX(translation);
+ mScrollView.setTranslationX(translation);
+ mHeader.setTranslationX(translation);
+ }
}
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 b6dbfce..c854d63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3249,6 +3249,10 @@
mKeyguardFadingAway = false;
}
+ public void stopWaitingForKeyguardExit() {
+ mWaitingForKeyguardExit = false;
+ }
+
private void updatePublicMode() {
setLockscreenPublicMode(
mStatusBarKeyguardViewManager.isShowing() && mStatusBarKeyguardViewManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 194a19a..0caf51a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -145,6 +145,7 @@
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard();
+ mPhoneStatusBar.stopWaitingForKeyguardExit();
mBouncer.hide(false /* destroyView */);
} else {
showBouncerOrKeyguard();
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 5d02576..0e3867d 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -566,13 +566,22 @@
}
void restorePendingWhileIdleAlarmsLocked() {
+ // Bring pending alarms back into the main list.
final long nowElapsed = SystemClock.elapsedRealtime();
- for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil != null; i --) {
+ for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil == null; i--) {
Alarm a = mPendingWhileIdleAlarms.remove(i);
reAddAlarmLocked(a, nowElapsed, false);
}
+
+ // Reschedule everything.
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
+
+ // And send a TIME_TICK right now, since it is important to get the UI updated.
+ try {
+ mTimeTickSender.send();
+ } catch (PendingIntent.CanceledException e) {
+ }
}
static final class InFlight extends Intent {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a31a1a7..5df74c5 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -220,6 +220,35 @@
setString("migrated_biometric_weak", "true", 0);
Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
}
+
+ // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
+ // user was present on the system, so if we're upgrading to M and there is more than one
+ // user we disable the flag to remain consistent.
+ if (getString("migrated_lockscreen_disabled", null, 0) == null) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+
+ final List<UserInfo> users = um.getUsers();
+ final int userCount = users.size();
+ int switchableUsers = 0;
+ for (int i = 0; i < userCount; i++) {
+ if (users.get(i).supportsSwitchTo()) {
+ switchableUsers++;
+ }
+ }
+
+ if (switchableUsers > 1) {
+ for (int i = 0; i < userCount; i++) {
+ int id = users.get(i).id;
+
+ if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
+ setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+ }
+ }
+ }
+
+ setString("migrated_lockscreen_disabled", "true", 0);
+ Slog.i(TAG, "Migrated lockscreen disabled flag");
+ }
} catch (RemoteException re) {
Slog.e(TAG, "Unable to migrate old data", re);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d5ae8e..a48a4d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -437,6 +437,11 @@
*/
SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+ /**
+ * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
+ */
+ String mDeviceOwnerName;
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -4831,6 +4836,9 @@
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
+ if (packageName != null && packageName.equals(mDeviceOwnerName)) {
+ throw new SecurityException("Clearing DeviceOwner data is forbidden.");
+ }
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
userId = handleIncomingUser(pid, uid,
@@ -8563,6 +8571,17 @@
}
@Override
+ public void updateDeviceOwner(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("updateDeviceOwner called from non-system process");
+ }
+ synchronized (this) {
+ mDeviceOwnerName = packageName;
+ }
+ }
+
+ @Override
public void updateLockTaskPackages(int userId, String[] packages) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b3aa966..252c16a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4275,6 +4275,8 @@
Slog.wtf(PackageManagerService.TAG,
"Failed to write settings, restoring backup", t);
destination.failWrite(out);
+ throw new IllegalStateException("Failed to write runtime permissions,"
+ + " restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
@@ -4322,10 +4324,9 @@
parser.setInput(in, null);
parseRuntimePermissionsLPr(parser, userId);
- // Any error while parsing is fatal.
- } catch (Throwable t) {
+ } catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing permissions file: "
- + permissionsFile , t);
+ + permissionsFile , e);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9bb97f7..eb9234a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1106,6 +1106,7 @@
void loadDeviceOwner() {
synchronized (this) {
mDeviceOwner = DeviceOwner.load();
+ updateDeviceOwnerLocked();
}
}
@@ -1629,20 +1630,25 @@
// sufficiently what is currently set. Note that this is only
// a sanity check in case the two get out of sync; this should
// never normally happen.
- LockPatternUtils utils = new LockPatternUtils(mContext);
- if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
- Slog.w(LOG_TAG, "Active password quality 0x"
- + Integer.toHexString(policy.mActivePasswordQuality)
- + " does not match actual quality 0x"
- + Integer.toHexString(utils.getActivePasswordQuality(userHandle)));
- policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- policy.mActivePasswordLength = 0;
- policy.mActivePasswordUpperCase = 0;
- policy.mActivePasswordLowerCase = 0;
- policy.mActivePasswordLetters = 0;
- policy.mActivePasswordNumeric = 0;
- policy.mActivePasswordSymbols = 0;
- policy.mActivePasswordNonLetter = 0;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
+ Slog.w(LOG_TAG, "Active password quality 0x"
+ + Integer.toHexString(policy.mActivePasswordQuality)
+ + " does not match actual quality 0x"
+ + Integer.toHexString(utils.getActivePasswordQuality(userHandle)));
+ policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ policy.mActivePasswordLength = 0;
+ policy.mActivePasswordUpperCase = 0;
+ policy.mActivePasswordLowerCase = 0;
+ policy.mActivePasswordLetters = 0;
+ policy.mActivePasswordNumeric = 0;
+ policy.mActivePasswordSymbols = 0;
+ policy.mActivePasswordNonLetter = 0;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
validatePasswordOwnerLocked(policy);
@@ -1667,6 +1673,18 @@
}
}
+ private void updateDeviceOwnerLocked() {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ am.updateDeviceOwner(getDeviceOwner());
+ } catch (RemoteException e) {
+ // Not gonna happen.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
static void validateQualityConstant(int quality) {
switch (quality) {
case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
@@ -3990,14 +4008,13 @@
if (mDeviceOwner == null) {
// Device owner is not set and does not exist, set it.
mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
} else {
// Device owner is not set but a profile owner exists, update Device owner state.
mDeviceOwner.setDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
}
+ mDeviceOwner.writeOwnerFile();
+ updateDeviceOwnerLocked();
+ return true;
}
}
@@ -4079,6 +4096,7 @@
if (mDeviceOwner != null) {
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
+ updateDeviceOwnerLocked();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4107,15 +4125,13 @@
if (mDeviceOwner == null) {
// Device owner state does not exist, create it.
- mDeviceOwner = DeviceOwner.createWithDeviceInitializer(
- initializer, ownerName);
+ mDeviceOwner = DeviceOwner.createWithDeviceInitializer(initializer, ownerName);
} else {
// Device owner already exists, update it.
mDeviceOwner.setDeviceInitializer(initializer, ownerName);
}
addDeviceInitializerToLockTaskPackagesLocked(UserHandle.USER_OWNER);
-
mDeviceOwner.writeOwnerFile();
return true;
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index bbd5e30..2a30384 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -66,9 +66,18 @@
public static final int STATE_DISCONNECTED = 7;
/**
- * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+ * The state of an outgoing {@code Call} when waiting on user to select a
+ * {@link PhoneAccount} through which to place the call.
*/
- public static final int STATE_PRE_DIAL_WAIT = 8;
+ public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
+
+ /**
+ * @hide
+ * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
+ */
+ @Deprecated
+ @SystemApi
+ public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
/**
* The initial state of an outgoing {@code Call}.
@@ -929,7 +938,7 @@
mVideoCall = parcelableCall.getVideoCall();
}
- int state = stateFromParcelableCallState(parcelableCall.getState());
+ int state = parcelableCall.getState();
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
@@ -1064,32 +1073,4 @@
callback.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
}
}
-
- private int stateFromParcelableCallState(int parcelableCallState) {
- switch (parcelableCallState) {
- case CallState.NEW:
- return STATE_NEW;
- case CallState.CONNECTING:
- return STATE_CONNECTING;
- case CallState.PRE_DIAL_WAIT:
- return STATE_PRE_DIAL_WAIT;
- case CallState.DIALING:
- return STATE_DIALING;
- case CallState.RINGING:
- return STATE_RINGING;
- case CallState.ACTIVE:
- return STATE_ACTIVE;
- case CallState.ON_HOLD:
- return STATE_HOLDING;
- case CallState.DISCONNECTED:
- return STATE_DISCONNECTED;
- case CallState.ABORTED:
- return STATE_DISCONNECTED;
- case CallState.DISCONNECTING:
- return STATE_DISCONNECTING;
- default:
- Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
- return STATE_NEW;
- }
- }
}
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
deleted file mode 100644
index 5584226..0000000
--- a/telecomm/java/android/telecom/CallState.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 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.telecom;
-
-/**
- * Defines call-state constants of the different states in which a call can exist. Although states
- * have the notion of normal transitions, due to the volatile nature of telephony systems, code
- * that uses these states should be resilient to unexpected state changes outside of what is
- * considered traditional.
- */
-public final class CallState {
-
- private CallState() {}
-
- /**
- * Indicates that a call is new and not connected. This is used as the default state internally
- * within Telecom and should not be used between Telecom and call services. Call services are
- * not expected to ever interact with NEW calls, but {@link InCallService}s will see calls in
- * this state.
- */
- public static final int NEW = 0;
-
- /**
- * The initial state of an outgoing {@code Call}.
- * Common transitions are to {@link #DIALING} state for a successful call or
- * {@link #DISCONNECTED} if it failed.
- */
- public static final int CONNECTING = 1;
-
- /**
- * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
- * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
- * this is the state where the InCallUI is waiting for the user to select a
- * {@link PhoneAccount} to call from.
- */
- public static final int PRE_DIAL_WAIT = 2;
-
- /**
- * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
- * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
- * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
- * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
- */
- public static final int DIALING = 3;
-
- /**
- * Indicates that a call is incoming and the user still has the option of answering, rejecting,
- * or doing nothing with the call. This state is usually associated with some type of audible
- * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
- * otherwise.
- */
- public static final int RINGING = 4;
-
- /**
- * Indicates that a call is currently connected to another party and a communication channel is
- * open between them. The normal transition to this state is by the user answering a
- * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
- */
- public static final int ACTIVE = 5;
-
- /**
- * Indicates that the call is currently on hold. In this state, the call is not terminated
- * but no communication is allowed until the call is no longer on hold. The typical transition
- * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
- * an action, such as clicking the hold button.
- */
- public static final int ON_HOLD = 6;
-
- /**
- * Indicates that a call is currently disconnected. All states can transition to this state
- * by the call service giving notice that the connection has been severed. When the user
- * explicitly ends a call, it will not transition to this state until the call service confirms
- * the disconnection or communication was lost to the call service currently responsible for
- * this call (e.g., call service crashes).
- */
- public static final int DISCONNECTED = 7;
-
- /**
- * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
- * time of writing) but cancelled before it was successfully connected.
- */
- public static final int ABORTED = 8;
-
- /**
- * Indicates that the call is in the process of being disconnected and will transition next
- * to a {@link #DISCONNECTED} state.
- * <p>
- * This state is not expected to be communicated from the Telephony layer, but will be reported
- * to the InCall UI for calls where disconnection has been initiated by the user but the
- * ConnectionService has confirmed the call as disconnected.
- */
- public static final int DISCONNECTING = 9;
-
- public static String toString(int callState) {
- switch (callState) {
- case NEW:
- return "NEW";
- case CONNECTING:
- return "CONNECTING";
- case PRE_DIAL_WAIT:
- return "PRE_DIAL_WAIT";
- case DIALING:
- return "DIALING";
- case RINGING:
- return "RINGING";
- case ACTIVE:
- return "ACTIVE";
- case ON_HOLD:
- return "ON_HOLD";
- case DISCONNECTED:
- return "DISCONNECTED";
- case ABORTED:
- return "ABORTED";
- case DISCONNECTING:
- return "DISCONNECTING";
- default:
- return "UNKNOWN";
- }
- }
-}
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
new file mode 100644
index 0000000..eef72fb
--- /dev/null
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 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.telecom;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for managing the default dialer application that will receive incoming calls, and be
+ * allowed to make emergency outgoing calls.
+ *
+ * @hide
+ */
+public class DefaultDialerManager {
+ private static final String TAG = "DefaultDialerManager";
+
+ /**
+ * Sets the specified package name as the default dialer application. The caller of this method
+ * needs to have permission to write to secure settings.
+ *
+ * @hide
+ * */
+ public static void setDefaultPhoneApplication(Context context, String packageName) {
+ // Get old package name
+ String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION);
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return;
+ }
+
+ // Only make the change if the new package belongs to a valid phone application
+ List<ComponentName> componentNames = getInstalledDialerApplications(context);
+ final ComponentName foundComponent = getComponentName(componentNames, packageName);
+
+ if (foundComponent != null) {
+ // Update the secure setting.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, foundComponent.getPackageName());
+ }
+ }
+
+ /**
+ * Returns the installed dialer application that will be used to receive incoming calls, and is
+ * allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * @hide
+ * */
+ public static ComponentName getDefaultDialerApplication(Context context) {
+ String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION);
+
+ final List<ComponentName> componentNames = getInstalledDialerApplications(context);
+ if (!TextUtils.isEmpty(defaultPackageName)) {
+ final ComponentName defaultDialer =
+ getComponentName(componentNames, defaultPackageName);
+ if (defaultDialer != null) {
+ return defaultDialer;
+ }
+ }
+
+ // No user-set dialer found, fallback to system dialer
+ ComponentName systemDialer = getTelecomManager(context).getDefaultPhoneApp();
+
+ if (systemDialer == null) {
+ // No system dialer configured at build time
+ return null;
+ }
+
+ // Verify that the system dialer has not been disabled.
+ return getComponentName(componentNames, systemDialer.getPackageName());
+ }
+
+ /**
+ * Returns a list of installed and available dialer applications.
+ *
+ * In order to appear in the list, a dialer application must implement an intent-filter with
+ * the DIAL intent for the following schemes:
+ *
+ * 1) Empty scheme
+ * 2) tel Uri scheme
+ *
+ * @hide
+ **/
+ public static List<ComponentName> getInstalledDialerApplications(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for the DIAL intent with empty scheme
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+
+ List<ComponentName> componentNames = new ArrayList<ComponentName> ();
+
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final ComponentName componentName =
+ new ComponentName(activityInfo.packageName, activityInfo.name);
+ componentNames.add(componentName);
+ }
+
+ // TODO: Filter for apps that don't handle DIAL intent with tel scheme
+ return componentNames;
+ }
+
+ /**
+ * Returns the {@link ComponentName} for the installed dialer application for a given package
+ * name.
+ *
+ * @param context A valid context.
+ * @param packageName to retrieve the {@link ComponentName} for.
+ *
+ * @return The {@link ComponentName} for the installed dialer application corresponding to the
+ * package name, or null if none is found.
+ *
+ * @hide
+ */
+ public static ComponentName getDialerApplicationForPackageName(Context context,
+ String packageName) {
+ return getComponentName(getInstalledDialerApplications(context), packageName);
+ }
+
+ /**
+ * Returns the component from a list of application components that corresponds to the package
+ * name.
+ *
+ * @param componentNames A list of component names
+ * @param packageName The package name to look for
+ * @return The {@link ComponentName} that matches the provided packageName, or null if not
+ * found.
+ */
+ private static ComponentName getComponentName(List<ComponentName> componentNames,
+ String packageName) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(packageName, componentName.getPackageName())) {
+ return componentName;
+ }
+ }
+ return null;
+ }
+
+ private static TelecomManager getTelecomManager(Context context) {
+ return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72172c..fd95327 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -17,6 +17,7 @@
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -1057,6 +1058,39 @@
}
}
+ /**
+ * Places a new outgoing call to the provided address using the system telecom service with
+ * the specified extras.
+ *
+ * This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
+ * except that the outgoing call will always be sent via the system telecom service. If
+ * method-caller is either the user selected default dialer app or preloaded system dialer
+ * app, then emergency calls will also be allowed.
+ *
+ * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+ *
+ * Usage example:
+ * <pre>
+ * Uri uri = Uri.fromParts("tel", "12345", null);
+ * Bundle extras = new Bundle();
+ * extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
+ * telecomManager.placeCall(uri, extras);
+ * </pre>
+ *
+ * @param address The address to make the call to.
+ * @param extras Bundle of extras to use with the call.
+ */
+ public void placeCall(Uri address, Bundle extras) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.placeCall(address, extras, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+ }
+ }
+ }
+
private ITelecomService getTelecomService() {
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 727fd4bb..45b2482 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -210,4 +210,9 @@
* @see TelecomServiceImpl#addNewUnknownCall
*/
void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+ /**
+ * @see TelecomServiceImpl#placeCall
+ */
+ void placeCall(in Uri handle, in Bundle extras, String callingPackage);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d103fbf..7d1a2fa 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -71,6 +71,11 @@
public static final String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
/**
+ * Control whether users can edit APNs in Settings.
+ */
+ public static final String BOOL_APN_EXPAND = "bool_apn_expand";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -121,6 +126,7 @@
sDefaults.putBoolean(BOOL_CARRIER_VOLTE_PROVISIONED, false);
sDefaults.putBoolean(BOOL_CARRIER_VOLTE_TTY_SUPPORTED, true);
sDefaults.putBoolean(BOOL_SHOW_APN_SETTING_CDMA, false);
+ sDefaults.putBoolean(BOOL_APN_EXPAND, true);
sDefaults.putInt(INT_VOLTE_REPLACEMENT_RAT, 0);
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index d10a7ea..9fea418 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -48,6 +48,13 @@
public static final int RAF_GSM = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
public static final int RAF_TD_SCDMA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA);
+ // Grouping of RAFs
+ private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
+ private static final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
+ private static final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
+ private static final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD;
+ private static final int WCDMA = HS | RAF_UMTS;
+
/* Phone ID of phone */
private int mPhoneId;
@@ -136,12 +143,6 @@
};
public static int getRafFromNetworkType(int type) {
- final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
- final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
- final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
- final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B;
- final int WCDMA = HS | RAF_UMTS;
-
int raf;
switch (type) {
@@ -158,7 +159,7 @@
raf = GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_CDMA:
- raf = CDMA;
+ raf = CDMA | EVDO;
break;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
raf = RAF_LTE | CDMA | EVDO;
@@ -188,7 +189,71 @@
raf = RAF_UNKNOWN;
break;
}
+
return raf;
}
+
+ /**
+ * if the raf includes ANY bit set for a group
+ * adjust it to contain ALL the bits for that group
+ */
+ private static int getAdjustedRaf(int raf) {
+ raf = ((GSM & raf) > 0) ? (GSM | raf) : raf;
+ raf = ((WCDMA & raf) > 0) ? (WCDMA | raf) : raf;
+ raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
+ raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
+
+ return raf;
+ }
+
+ public static int getNetworkTypeFromRaf(int raf) {
+ int type;
+
+ raf = getAdjustedRaf(raf);
+
+ switch (raf) {
+ case (GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_WCDMA_PREF;
+ break;
+ case GSM:
+ type = RILConstants.NETWORK_MODE_GSM_ONLY;
+ break;
+ case WCDMA:
+ type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+ break;
+ case (CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_CDMA;
+ break;
+ case (RAF_LTE | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+ break;
+ case (RAF_LTE | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+ break;
+ case (RAF_LTE | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+ break;
+ case RAF_LTE:
+ type = RILConstants.NETWORK_MODE_LTE_ONLY;
+ break;
+ case (RAF_LTE | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_WCDMA;
+ break;
+ case CDMA:
+ type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+ break;
+ case EVDO:
+ type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+ break;
+ case (GSM | WCDMA | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_GLOBAL;
+ break;
+ default:
+ type = RILConstants.PREFERRED_NETWORK_MODE ;
+ break;
+ }
+
+ return type;
+ }
}