Merge change 2017 into donut
* changes:
Increment BatteryStatsImpl's VERSION. That'll make it stop trying to interpret older records with the new format. Also applied other comments involving name changes to remove un-needed 'Wifi' labels in WifiManager API, etc.
diff --git a/api/current.xml b/api/current.xml
index b743b03..6fe382d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1035,6 +1035,17 @@
visibility="public"
>
</field>
+<field name="STOP_APP_SWITCHES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.permission.STOP_APP_SWITCHES""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUBSCRIBED_FEEDS_READ"
type="java.lang.String"
transient="false"
@@ -1471,6 +1482,17 @@
visibility="public"
>
</field>
+<field name="linear_interpolator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432587"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="overshoot_interpolator"
type="int"
transient="false"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 6d4b455..88ad265 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -194,18 +194,17 @@
if (intent != null) {
System.out.println("Starting: " + intent);
try {
- intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// XXX should do something to determine the MIME type.
int res = mAm.startActivity(null, intent, intent.getType(),
null, 0, null, null, 0, false, mDebugOption);
switch (res) {
case IActivityManager.START_SUCCESS:
break;
- case IActivityManager.START_CLASS_NOT_FOUND:
- System.err.println("Error type 3");
- System.err.println("Error: Activity class " +
- intent.getComponent().toShortString()
- + " does not exist.");
+ case IActivityManager.START_SWITCHES_CANCELED:
+ System.err.println(
+ "Warning: Activity not started because the "
+ + " current activity is being kept for the user.");
break;
case IActivityManager.START_DELIVERED_TO_TOP:
System.err.println(
@@ -213,16 +212,6 @@
+ "been delivered to currently running "
+ "top-most instance.");
break;
- case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
- System.err.println(
- "Error: Activity not started, you requested to "
- + "both forward and receive its result");
- break;
- case IActivityManager.START_INTENT_NOT_RESOLVED:
- System.err.println(
- "Error: Activity not started, unable to "
- + "resolve " + intent.toString());
- break;
case IActivityManager.START_RETURN_INTENT_TO_CALLER:
System.err.println(
"Warning: Activity not started because intent "
@@ -233,6 +222,27 @@
"Warning: Activity not started, its current "
+ "task has been brought to the front");
break;
+ case IActivityManager.START_INTENT_NOT_RESOLVED:
+ System.err.println(
+ "Error: Activity not started, unable to "
+ + "resolve " + intent.toString());
+ break;
+ case IActivityManager.START_CLASS_NOT_FOUND:
+ System.err.println("Error type 3");
+ System.err.println("Error: Activity class " +
+ intent.getComponent().toShortString()
+ + " does not exist.");
+ break;
+ case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
+ System.err.println(
+ "Error: Activity not started, you requested to "
+ + "both forward and receive its result");
+ break;
+ case IActivityManager.START_PERMISSION_DENIED:
+ System.err.println(
+ "Error: Activity not started, you do not "
+ + "have permission to access it.");
+ break;
default:
System.err.println(
"Error: Activity not started, unknown error "
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 541f413..16f0a30 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -998,6 +998,20 @@
return true;
}
+ case STOP_APP_SWITCHES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ stopAppSwitches();
+ reply.writeNoException();
+ return true;
+ }
+
+ case RESUME_APP_SWITCHES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ resumeAppSwitches();
+ reply.writeNoException();
+ return true;
+ }
+
case PEEK_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
Intent service = Intent.CREATOR.createFromParcel(data);
@@ -2182,5 +2196,25 @@
return res;
}
+ public void stopAppSwitches() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(STOP_APP_SWITCHES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ reply.recycle();
+ data.recycle();
+ }
+
+ public void resumeAppSwitches() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(RESUME_APP_SWITCHES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ reply.recycle();
+ data.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
new file mode 100644
index 0000000..72cbff4
--- /dev/null
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2008 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.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.Printer;
+
+/**
+ * Describes an application error.
+ *
+ * A report has a type, which is one of
+ * <ul>
+ * <li> {@link #TYPE_CRASH} application crash. Information about the crash
+ * is stored in {@link #crashInfo}.
+ * <li> {@link #TYPE_ANR} application not responding. Information about the
+ * ANR is stored in {@link #anrInfo}.
+ * <li> {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}.
+ * </ul>
+ *
+ * @hide
+ */
+
+public class ApplicationErrorReport implements Parcelable {
+ /**
+ * Uninitialized error report.
+ */
+ public static final int TYPE_NONE = 0;
+
+ /**
+ * An error report about an application crash.
+ */
+ public static final int TYPE_CRASH = 1;
+
+ /**
+ * An error report about an application that's not responding.
+ */
+ public static final int TYPE_ANR = 2;
+
+ /**
+ * Type of this report. Can be one of {@link #TYPE_NONE},
+ * {@link #TYPE_CRASH} or {@link #TYPE_ANR}.
+ */
+ public int type;
+
+ /**
+ * Package name of the application.
+ */
+ public String packageName;
+
+ /**
+ * Package name of the application which installed the application this
+ * report pertains to.
+ * This identifies which Market the application came from.
+ */
+ public String installerPackageName;
+
+ /**
+ * Process name of the application.
+ */
+ public String processName;
+
+ /**
+ * Time at which the error occurred.
+ */
+ public long time;
+
+ /**
+ * If this report is of type {@link #TYPE_CRASH}, contains an instance
+ * of CrashInfo describing the crash; otherwise null.
+ */
+ public CrashInfo crashInfo;
+
+ /**
+ * If this report is of type {@link #TYPE_ANR}, contains an instance
+ * of AnrInfo describing the ANR; otherwise null.
+ */
+ public AnrInfo anrInfo;
+
+ /**
+ * Create an uninitialized instance of {@link ApplicationErrorReport}.
+ */
+ public ApplicationErrorReport() {
+ }
+
+ /**
+ * Create an instance of {@link ApplicationErrorReport} initialized from
+ * a parcel.
+ */
+ ApplicationErrorReport(Parcel in) {
+ type = in.readInt();
+ packageName = in.readString();
+ installerPackageName = in.readString();
+ processName = in.readString();
+ time = in.readLong();
+
+ switch (type) {
+ case TYPE_CRASH:
+ crashInfo = new CrashInfo(in);
+ break;
+ case TYPE_ANR:
+ anrInfo = new AnrInfo(in);
+ break;
+ }
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(type);
+ dest.writeString(packageName);
+ dest.writeString(installerPackageName);
+ dest.writeString(processName);
+ dest.writeLong(time);
+
+ switch (type) {
+ case TYPE_CRASH:
+ crashInfo.writeToParcel(dest, flags);
+ break;
+ case TYPE_ANR:
+ anrInfo.writeToParcel(dest, flags);
+ break;
+ }
+ }
+
+ /**
+ * Describes an application crash.
+ */
+ public static class CrashInfo {
+ /**
+ * Class name of the exception that caused the crash.
+ */
+ public String exceptionClassName;
+
+ /**
+ * File which the exception was thrown from.
+ */
+ public String throwFileName;
+
+ /**
+ * Class which the exception was thrown from.
+ */
+ public String throwClassName;
+
+ /**
+ * Method which the exception was thrown from.
+ */
+ public String throwMethodName;
+
+ /**
+ * Stack trace.
+ */
+ public String stackTrace;
+
+ /**
+ * Create an uninitialized instance of CrashInfo.
+ */
+ public CrashInfo() {
+ }
+
+ /**
+ * Create an instance of CrashInfo initialized from a Parcel.
+ */
+ public CrashInfo(Parcel in) {
+ exceptionClassName = in.readString();
+ throwFileName = in.readString();
+ throwClassName = in.readString();
+ throwMethodName = in.readString();
+ stackTrace = in.readString();
+ }
+
+ /**
+ * Save a CrashInfo instance to a parcel.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(exceptionClassName);
+ dest.writeString(throwFileName);
+ dest.writeString(throwClassName);
+ dest.writeString(throwMethodName);
+ dest.writeString(stackTrace);
+ }
+
+ /**
+ * Dump a CrashInfo instance to a Printer.
+ */
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "exceptionClassName: " + exceptionClassName);
+ pw.println(prefix + "throwFileName: " + throwFileName);
+ pw.println(prefix + "throwClassName: " + throwClassName);
+ pw.println(prefix + "throwMethodName: " + throwMethodName);
+ pw.println(prefix + "stackTrace: " + stackTrace);
+ }
+ }
+
+ /**
+ * Describes an application not responding error.
+ */
+ public static class AnrInfo {
+ /**
+ * Activity name.
+ */
+ public String activity;
+
+ /**
+ * Description of the operation that timed out.
+ */
+ public String cause;
+
+ /**
+ * Additional info, including CPU stats.
+ */
+ public String info;
+
+ /**
+ * Create an uninitialized instance of AnrInfo.
+ */
+ public AnrInfo() {
+ }
+
+ /**
+ * Create an instance of AnrInfo initialized from a Parcel.
+ */
+ public AnrInfo(Parcel in) {
+ activity = in.readString();
+ cause = in.readString();
+ info = in.readString();
+ }
+
+ /**
+ * Save an AnrInfo instance to a parcel.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(activity);
+ dest.writeString(cause);
+ dest.writeString(info);
+ }
+
+ /**
+ * Dump an AnrInfo instance to a Printer.
+ */
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "activity: " + activity);
+ pw.println(prefix + "cause: " + cause);
+ pw.println(prefix + "info: " + info);
+ }
+ }
+
+ public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
+ = new Parcelable.Creator<ApplicationErrorReport>() {
+ public ApplicationErrorReport createFromParcel(Parcel source) {
+ return new ApplicationErrorReport(source);
+ }
+
+ public ApplicationErrorReport[] newArray(int size) {
+ return new ApplicationErrorReport[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Dump the report to a Printer.
+ */
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "type: " + type);
+ pw.println(prefix + "packageName: " + packageName);
+ pw.println(prefix + "installerPackageName: " + installerPackageName);
+ pw.println(prefix + "processName: " + processName);
+ pw.println(prefix + "time: " + time);
+
+ switch (type) {
+ case TYPE_CRASH:
+ crashInfo.dump(pw, prefix);
+ break;
+ case TYPE_ANR:
+ anrInfo.dump(pw, prefix);
+ break;
+ }
+ }
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 56b29c1..d15a154 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -44,9 +44,30 @@
* {@hide}
*/
public interface IActivityManager extends IInterface {
+ /**
+ * Returned by startActivity() if the start request was canceled because
+ * app switches are temporarily canceled to ensure the user's last request
+ * (such as pressing home) is performed.
+ */
+ public static final int START_SWITCHES_CANCELED = 4;
+ /**
+ * Returned by startActivity() if an activity wasn't really started, but
+ * the given Intent was given to the existing top activity.
+ */
public static final int START_DELIVERED_TO_TOP = 3;
+ /**
+ * Returned by startActivity() if an activity wasn't really started, but
+ * a task was simply brought to the foreground.
+ */
public static final int START_TASK_TO_FRONT = 2;
+ /**
+ * Returned by startActivity() if the caller asked that the Intent not
+ * be executed if it is the recipient, and that is indeed the case.
+ */
public static final int START_RETURN_INTENT_TO_CALLER = 1;
+ /**
+ * Activity was started successfully as normal.
+ */
public static final int START_SUCCESS = 0;
public static final int START_INTENT_NOT_RESOLVED = -1;
public static final int START_CLASS_NOT_FOUND = -2;
@@ -225,6 +246,9 @@
public boolean shutdown(int timeout) throws RemoteException;
+ public void stopAppSwitches() throws RemoteException;
+ public void resumeAppSwitches() throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -371,4 +395,6 @@
int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84;
int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85;
int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86;
+ int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87;
+ int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88;
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 30acef9..333c7cb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -604,6 +604,20 @@
*/
public static final native void setThreadGroup(int tid, int group)
throws IllegalArgumentException, SecurityException;
+ /**
+ * Sets the scheduling group for a process and all child threads
+ * @hide
+ * @param pid The indentifier of the process to change.
+ * @param group The target group for this process.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist.
+ * @throws SecurityException Throws SecurityException if your process does
+ * not have permission to modify the given thread, or to use the given
+ * priority.
+ */
+ public static final native void setProcessGroup(int pid, int group)
+ throws IllegalArgumentException, SecurityException;
/**
* Set the priority of the calling thread, based on Linux priorities. See
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a47b42..56a0104 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -148,7 +148,7 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WIFI_SETTINGS =
"android.settings.WIFI_SETTINGS";
-
+
/**
* Activity Action: Show settings to allow configuration of a static IP
* address for Wi-Fi.
@@ -305,7 +305,7 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_QUICK_LAUNCH_SETTINGS =
"android.settings.QUICK_LAUNCH_SETTINGS";
-
+
/**
* Activity Action: Show settings to manage installed applications.
* <p>
@@ -319,7 +319,7 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS =
"android.settings.MANAGE_APPLICATIONS_SETTINGS";
-
+
/**
* Activity Action: Show settings for system update functionality.
* <p>
@@ -329,7 +329,7 @@
* Input: Nothing.
* <p>
* Output: Nothing.
- *
+ *
* @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -349,7 +349,7 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SYNC_SETTINGS =
"android.settings.SYNC_SETTINGS";
-
+
/**
* Activity Action: Show settings for selecting the network operator.
* <p>
@@ -404,7 +404,7 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MEMORY_CARD_SETTINGS =
"android.settings.MEMORY_CARD_SETTINGS";
-
+
// End of Intent actions for Settings
private static final String JID_RESOURCE_PREFIX = "android";
@@ -495,7 +495,7 @@
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
private static volatile NameValueCache mNameValueCache = null;
-
+
private static final HashSet<String> MOVED_TO_SECURE;
static {
MOVED_TO_SECURE = new HashSet<String>(30);
@@ -901,12 +901,12 @@
* plugged in.
*/
public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
-
+
/**
* Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep.
*/
public static final int WIFI_SLEEP_POLICY_NEVER = 2;
-
+
/**
* Whether to use static IP and other static network attributes.
* <p>
@@ -1115,12 +1115,12 @@
* Note: This is a one-off setting that will be removed in the future
* when there is profile support. For this reason, it is kept hidden
* from the public APIs.
- *
+ *
* @hide
*/
- public static final String NOTIFICATIONS_USE_RING_VOLUME =
+ public static final String NOTIFICATIONS_USE_RING_VOLUME =
"notifications_use_ring_volume";
-
+
/**
* The mapping of stream type (integer) to its setting.
*/
@@ -1188,7 +1188,7 @@
* feature converts two spaces to a "." and space.
*/
public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
-
+
/**
* Setting to showing password characters in text editors. 1 = On, 0 = Off
*/
@@ -1270,13 +1270,13 @@
* boolean (1 or 0).
*/
public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
-
+
/**
* Whether the haptic feedback (long presses, ...) are enabled. The value is
* boolean (1 or 0).
*/
public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
-
+
// Settings moved to Settings.Secure
/**
@@ -1321,7 +1321,7 @@
*/
@Deprecated
public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
-
+
/**
* @deprecated Use {@link android.provider.Settings.Secure#LOCATION_PROVIDERS_ALLOWED}
* instead
@@ -1334,7 +1334,7 @@
*/
@Deprecated
public static final String LOGGING_ID = Secure.LOGGING_ID;
-
+
/**
* @deprecated Use {@link android.provider.Settings.Secure#NETWORK_PREFERENCE} instead
*/
@@ -1374,7 +1374,7 @@
*/
@Deprecated
public static final String USB_MASS_STORAGE_ENABLED = Secure.USB_MASS_STORAGE_ENABLED;
-
+
/**
* @deprecated Use {@link android.provider.Settings.Secure#USE_GOOGLE_MAIL} instead
*/
@@ -1412,7 +1412,7 @@
@Deprecated
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
-
+
/**
* @deprecated Use {@link android.provider.Settings.Secure#WIFI_NUM_OPEN_NETWORKS_KEPT}
* instead
@@ -1448,7 +1448,7 @@
@Deprecated
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS;
-
+
/**
* @deprecated Use
* {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead
@@ -1824,19 +1824,19 @@
* Whether the device has been provisioned (0 = false, 1 = true)
*/
public static final String DEVICE_PROVISIONED = "device_provisioned";
-
+
/**
* List of input methods that are currently enabled. This is a string
* containing the IDs of all enabled input methods, each ID separated
* by ':'.
*/
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
-
+
/**
* Host name and port for a user-selected proxy.
*/
public static final String HTTP_PROXY = "http_proxy";
-
+
/**
* Whether the package installer should allow installation of apps downloaded from
* sources other than the Android Market (vending machine).
@@ -1845,12 +1845,12 @@
* 0 = only allow installing from the Android Market
*/
public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
-
+
/**
* Comma-separated list of location providers that activities may access.
*/
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
-
+
/**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
@@ -1872,19 +1872,19 @@
* connectivity service should touch this.
*/
public static final String NETWORK_PREFERENCE = "network_preference";
-
- /**
+
+ /**
*/
public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
-
- /**
+
+ /**
*/
public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
-
- /**
+
+ /**
*/
public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
-
+
/**
* Settings classname to launch when Settings is clicked from All
* Applications. Needed because of user testing between the old
@@ -1892,18 +1892,18 @@
*/
// TODO: 881807
public static final String SETTINGS_CLASSNAME = "settings_classname";
-
+
/**
* USB Mass Storage Enabled
*/
public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
-
+
/**
* If this setting is set (to anything), then all references
* to Gmail on the device must change to Google Mail.
*/
public static final String USE_GOOGLE_MAIL = "use_google_mail";
-
+
/**
* If accessibility is enabled.
*/
@@ -1926,64 +1926,64 @@
*/
public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
"wifi_networks_available_notification_on";
-
+
/**
* Delay (in seconds) before repeating the Wi-Fi networks available notification.
* Connecting to a network will reset the timer.
*/
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
"wifi_networks_available_repeat_delay";
-
+
/**
* The number of radio channels that are allowed in the local
* 802.11 regulatory domain.
* @hide
*/
public static final String WIFI_NUM_ALLOWED_CHANNELS = "wifi_num_allowed_channels";
-
+
/**
* When the number of open networks exceeds this number, the
* least-recently-used excess networks will be removed.
*/
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
-
+
/**
* Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this.
*/
public static final String WIFI_ON = "wifi_on";
-
+
/**
* The acceptable packet loss percentage (range 0 - 100) before trying
* another AP on the same network.
*/
public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
"wifi_watchdog_acceptable_packet_loss_percentage";
-
+
/**
* The number of access points required for a network in order for the
* watchdog to monitor it.
*/
public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count";
-
+
/**
* The delay between background checks.
*/
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
"wifi_watchdog_background_check_delay_ms";
-
+
/**
* Whether the Wi-Fi watchdog is enabled for background checking even
* after it thinks the user has connected to a good access point.
*/
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
"wifi_watchdog_background_check_enabled";
-
+
/**
* The timeout for a background ping
*/
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
"wifi_watchdog_background_check_timeout_ms";
-
+
/**
* The number of initial pings to perform that *may* be ignored if they
* fail. Again, if these fail, they will *not* be used in packet loss
@@ -1992,7 +1992,7 @@
*/
public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
"wifi_watchdog_initial_ignored_ping_count";
-
+
/**
* The maximum number of access points (per network) to attempt to test.
* If this number is reached, the watchdog will no longer monitor the
@@ -2000,7 +2000,7 @@
* networks containing multiple APs whose DNS does not respond to pings.
*/
public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks";
-
+
/**
* Whether the Wi-Fi watchdog is enabled.
*/
@@ -2015,24 +2015,24 @@
* The number of pings to test if an access point is a good connection.
*/
public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count";
-
+
/**
* The delay between pings.
*/
public static final String WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms";
-
+
/**
* The timeout per ping.
*/
public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms";
-
+
/**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
* A value of N means that we will make N+1 connection attempts in all.
*/
public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
-
+
/**
* Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile
* data connectivity to be established after a disconnect from Wi-Fi.
@@ -2062,21 +2062,14 @@
public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
/**
- * represents current active phone class
- * 1 = GSM-Phone, 0 = CDMA-Phone
- * @hide
- */
- public static final String CURRENT_ACTIVE_PHONE = "current_active_phone";
-
- /**
- * The preferred network mode 7 = Global
- * 6 = EvDo only
- * 5 = CDMA w/o EvDo
- * 4 = CDMA / EvDo auto
- * 3 = GSM / WCDMA auto
- * 2 = WCDMA only
- * 1 = GSM only
- * 0 = GSM / WCDMA preferred
+ * The preferred network mode 7 = Global
+ * 6 = EvDo only
+ * 5 = CDMA w/o EvDo
+ * 4 = CDMA / EvDo auto
+ * 3 = GSM / WCDMA auto
+ * 2 = WCDMA only
+ * 1 = GSM only
+ * 0 = GSM / WCDMA preferred
* @hide
*/
public static final String PREFERRED_NETWORK_MODE =
@@ -2142,7 +2135,7 @@
allowedProviders.startsWith(provider + ",") ||
allowedProviders.endsWith("," + provider));
}
- return false;
+ return false;
}
/**
@@ -2166,7 +2159,7 @@
putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);
}
}
-
+
/**
* Gservices settings, containing the network names for Google's
* various services. This table holds simple name/addr pairs.
@@ -2294,7 +2287,7 @@
* Event tags from the kernel event log to upload during checkin.
*/
public static final String CHECKIN_EVENTS = "checkin_events";
-
+
/**
* Event tags for list of services to upload during checkin.
*/
@@ -2984,7 +2977,7 @@
public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD =
"battery_discharge_duration_threshold";
public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold";
-
+
/**
* An email address that anr bugreports should be sent to.
*/
@@ -3128,7 +3121,7 @@
/**
* Add a new bookmark to the system.
- *
+ *
* @param cr The ContentResolver to query.
* @param intent The desired target of the bookmark.
* @param title Bookmark title that is shown to the user; null if none
@@ -3193,7 +3186,7 @@
/**
* Return the title as it should be displayed to the user. This takes
* care of localizing bookmarks that point to activities.
- *
+ *
* @param context A context.
* @param cursor A cursor pointing to the row whose title should be
* returned. The cursor must contain at least the {@link #TITLE}
@@ -3208,24 +3201,24 @@
throw new IllegalArgumentException(
"The cursor must contain the TITLE and INTENT columns.");
}
-
+
String title = cursor.getString(titleColumn);
if (!TextUtils.isEmpty(title)) {
return title;
}
-
+
String intentUri = cursor.getString(intentColumn);
if (TextUtils.isEmpty(intentUri)) {
return "";
}
-
+
Intent intent;
try {
intent = Intent.getIntent(intentUri);
} catch (URISyntaxException e) {
return "";
}
-
+
PackageManager packageManager = context.getPackageManager();
ResolveInfo info = packageManager.resolveActivity(intent, 0);
return info != null ? info.loadLabel(packageManager) : "";
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index 9586d56..62631d6 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
@@ -34,6 +35,8 @@
*/
public class Searchables {
+ private static final String LOG_TAG = "Searchables";
+
// static strings used for XML lookups, etc.
// TODO how should these be documented for the developer, in a more structured way than
// the current long wordy javadoc in SearchManager.java ?
@@ -184,7 +187,6 @@
* TODO: sort the list somehow? UI choice.
*/
public void buildSearchableList() {
-
// These will become the new values at the end of the method
HashMap<ComponentName, SearchableInfo> newSearchablesMap
= new HashMap<ComponentName, SearchableInfo>();
@@ -223,6 +225,11 @@
ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm);
SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity);
+ if (newDefaultSearchable == null) {
+ Log.w(LOG_TAG, "No searchable info found for new default searchable activity "
+ + globalSearchActivity);
+ }
+
// Store a consistent set of new values
synchronized (this) {
mSearchablesMap = newSearchablesMap;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 219afec..4297be0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1329,9 +1329,13 @@
} else {
// We need to retain the last set padding, so just clear
// out all of the fields in the existing structure.
+ if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null);
dr.mDrawableLeft = null;
+ if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null);
dr.mDrawableTop = null;
+ if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null);
dr.mDrawableRight = null;
+ if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null);
dr.mDrawableBottom = null;
dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
@@ -1344,19 +1348,32 @@
mDrawables = dr = new Drawables();
}
+ if (dr.mDrawableLeft != left && dr.mDrawableLeft != null) {
+ dr.mDrawableLeft.setCallback(null);
+ }
dr.mDrawableLeft = left;
+ if (dr.mDrawableTop != left && dr.mDrawableTop != null) {
+ dr.mDrawableTop.setCallback(null);
+ }
dr.mDrawableTop = top;
+ if (dr.mDrawableRight != left && dr.mDrawableRight != null) {
+ dr.mDrawableRight.setCallback(null);
+ }
dr.mDrawableRight = right;
+ if (dr.mDrawableBottom != left && dr.mDrawableBottom != null) {
+ dr.mDrawableBottom.setCallback(null);
+ }
dr.mDrawableBottom = bottom;
final Rect compoundRect = dr.mCompoundRect;
- int[] state = null;
+ int[] state;
state = getDrawableState();
if (left != null) {
left.setState(state);
left.copyBounds(compoundRect);
+ left.setCallback(this);
dr.mDrawableSizeLeft = compoundRect.width();
dr.mDrawableHeightLeft = compoundRect.height();
} else {
@@ -1366,6 +1383,7 @@
if (right != null) {
right.setState(state);
right.copyBounds(compoundRect);
+ right.setCallback(this);
dr.mDrawableSizeRight = compoundRect.width();
dr.mDrawableHeightRight = compoundRect.height();
} else {
@@ -1375,6 +1393,7 @@
if (top != null) {
top.setState(state);
top.copyBounds(compoundRect);
+ top.setCallback(this);
dr.mDrawableSizeTop = compoundRect.height();
dr.mDrawableWidthTop = compoundRect.width();
} else {
@@ -1384,6 +1403,7 @@
if (bottom != null) {
bottom.setState(state);
bottom.copyBounds(compoundRect);
+ bottom.setCallback(this);
dr.mDrawableSizeBottom = compoundRect.height();
dr.mDrawableWidthBottom = compoundRect.width();
} else {
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp
index 004b0e3..0858741 100644
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -176,7 +176,7 @@
{
int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency));
if (result) {
- return result;
+ return false;
}
return (sGpsInterface->start() == 0);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index d760feb..95c38dc 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -236,6 +236,36 @@
signalExceptionForGroupError(env, clazz, errno);
}
+void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
+{
+ DIR *d;
+ FILE *fp;
+ char proc_path[255];
+ struct dirent *de;
+
+ if (grp > ANDROID_TGROUP_MAX || grp < 0) {
+ signalExceptionForGroupError(env, clazz, EINVAL);
+ return;
+ }
+
+ sprintf(proc_path, "/proc/%d/task", pid);
+ if (!(d = opendir(proc_path))) {
+ signalExceptionForGroupError(env, clazz, errno);
+ return;
+ }
+
+ while ((de = readdir(d))) {
+ if (de->d_name[0] == '.')
+ continue;
+ if (add_pid_to_cgroup(atoi(de->d_name), grp)) {
+ signalExceptionForGroupError(env, clazz, errno);
+ closedir(d);
+ return;
+ }
+ }
+ closedir(d);
+}
+
void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
jint pid, jint pri)
{
@@ -820,6 +850,7 @@
{"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority},
{"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority},
{"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup},
+ {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup},
{"setOomAdj", "(II)Z", (void*)android_os_Process_setOomAdj},
{"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
{"setUid", "(I)I", (void*)android_os_Process_setUid},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ca2db11..b5f3a0f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -803,14 +803,22 @@
android:description="@string/permdesc_runSetActivityWatcher"
android:protectionLevel="signature" />
- <!-- Allows an application to watch and control how activities are
- started globally in the system. Only for is in debugging
- (usually the monkey command). -->
+ <!-- Allows an application to call the activity manager shutdown() API
+ to put the higher-level system there into a shutdown state. -->
<permission android:name="android.permission.SHUTDOWN"
android:label="@string/permlab_shutdown"
android:description="@string/permdesc_shutdown"
android:protectionLevel="signature" />
+ <!-- Allows an application to tell the activity manager to temporarily
+ stop application switches, putting it into a special mode that
+ prevents applications from immediately switching away from some
+ critical UI such as the home screen. -->
+ <permission android:name="android.permission.STOP_APP_SWITCHES"
+ android:label="@string/permlab_stopAppSwitches"
+ android:description="@string/permdesc_stopAppSwitches"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to retrieve the current state of keys and
switches. This is only for use by the system.-->
<permission android:name="android.permission.READ_INPUT_STATE"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b5808ea..af9dbcd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1106,6 +1106,7 @@
<public type="anim" name="overshoot_interpolator" id="0x010a0008" />
<public type="anim" name="anticipate_overshoot_interpolator" id="0x010a0009" />
<public type="anim" name="bounce_interpolator" id="0x010a000a" />
+ <public type="anim" name="linear_interpolator" id="0x010a000b" />
<public type="drawable" name="stat_sys_vp_phone_call" id="0x0108022d" />
<public type="drawable" name="stat_sys_vp_phone_call_on_hold" id="0x0108022e" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 058a445..0f146e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -473,6 +473,12 @@
state. Does not perform a complete shutdown.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_stopAppSwitches">prevent app switches</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_stopAppSwitches">Prevents the user from switching to
+ another application.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_runSetActivityWatcher">monitor and control all application launching</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_runSetActivityWatcher">Allows an application to
@@ -1751,6 +1757,8 @@
<string name="anr_process">Process <xliff:g id="process">%1$s</xliff:g> is not responding.</string>
<!-- Button allowing the user to close an application that is not responding. This will kill the application. -->
<string name="force_close">Force close</string>
+ <!-- Button allowing the user to send a bug report for application which has encountered an error. -->
+ <string name="report">Report</string>
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
<string name="wait">Wait</string>
<!-- Button allowing a developer to connect a debugger to an application that is not responding. -->
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp
index b6d5078c..16a4f2d 100644
--- a/libs/audioflinger/A2dpAudioInterface.cpp
+++ b/libs/audioflinger/A2dpAudioInterface.cpp
@@ -71,8 +71,8 @@
}
AudioStreamIn* A2dpAudioInterface::openInputStream(
- int format, int channelCount, uint32_t sampleRate, status_t *status,
- AudioSystem::audio_in_acoustics acoustics)
+ int inputSource, int format, int channelCount, uint32_t sampleRate,
+ status_t *status, AudioSystem::audio_in_acoustics acoustics)
{
if (status)
*status = -1;
diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h
index 7901a8c..091e775 100644
--- a/libs/audioflinger/A2dpAudioInterface.h
+++ b/libs/audioflinger/A2dpAudioInterface.h
@@ -55,6 +55,7 @@
status_t *status=0);
virtual AudioStreamIn* openInputStream(
+ int inputSource,
int format,
int channelCount,
uint32_t sampleRate,
diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h
index 9a94102..b72c94e 100644
--- a/libs/audioflinger/AudioDumpInterface.h
+++ b/libs/audioflinger/AudioDumpInterface.h
@@ -78,9 +78,9 @@
virtual status_t setParameter(const char* key, const char* value)
{return mFinalInterface->setParameter(key, value);}
- virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status,
- AudioSystem::audio_in_acoustics acoustics)
- {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status, acoustics);}
+ virtual AudioStreamIn* openInputStream(int inputSource, int format, int channelCount,
+ uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
+ { return mFinalInterface->openInputStream(inputSource, format, channelCount, sampleRate, status, acoustics); }
virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index b56221f..e4f4aad 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -2407,7 +2407,9 @@
LOGV("AudioRecordThread: loop starting");
if (mRecordTrack != 0) {
- input = mAudioHardware->openInputStream(mRecordTrack->format(),
+ input = mAudioHardware->openInputStream(
+ mRecordTrack->type(),
+ mRecordTrack->format(),
mRecordTrack->channelCount(),
mRecordTrack->sampleRate(),
&mStartStatus,
diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp
index 62beada..a97c0bc 100644
--- a/libs/audioflinger/AudioHardwareGeneric.cpp
+++ b/libs/audioflinger/AudioHardwareGeneric.cpp
@@ -30,6 +30,7 @@
#include <utils/String8.h>
#include "AudioHardwareGeneric.h"
+#include <media/AudioRecord.h>
namespace android {
@@ -93,9 +94,15 @@
}
AudioStreamIn* AudioHardwareGeneric::openInputStream(
- int format, int channelCount, uint32_t sampleRate, status_t *status,
- AudioSystem::audio_in_acoustics acoustics)
+ int inputSource, int format, int channelCount, uint32_t sampleRate,
+ status_t *status, AudioSystem::audio_in_acoustics acoustics)
{
+ // check for valid input source
+ if ((inputSource != AudioRecord::DEFAULT_INPUT) &&
+ (inputSource != AudioRecord::MIC_INPUT)) {
+ return 0;
+ }
+
AutoMutex lock(mLock);
// only one input stream allowed
diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h
index c949aa1..c89df87 100644
--- a/libs/audioflinger/AudioHardwareGeneric.h
+++ b/libs/audioflinger/AudioHardwareGeneric.h
@@ -112,6 +112,7 @@
status_t *status=0);
virtual AudioStreamIn* openInputStream(
+ int inputSource,
int format,
int channelCount,
uint32_t sampleRate,
diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp
index b13cb1c..c61e6e6 100644
--- a/libs/audioflinger/AudioHardwareStub.cpp
+++ b/libs/audioflinger/AudioHardwareStub.cpp
@@ -23,6 +23,7 @@
#include <utils/String8.h>
#include "AudioHardwareStub.h"
+#include <media/AudioRecord.h>
namespace android {
@@ -56,9 +57,15 @@
}
AudioStreamIn* AudioHardwareStub::openInputStream(
- int format, int channelCount, uint32_t sampleRate,
+ int inputSource, int format, int channelCount, uint32_t sampleRate,
status_t *status, AudioSystem::audio_in_acoustics acoustics)
{
+ // check for valid input source
+ if ((inputSource != AudioRecord::DEFAULT_INPUT) &&
+ (inputSource != AudioRecord::MIC_INPUT)) {
+ return 0;
+ }
+
AudioStreamInStub* in = new AudioStreamInStub();
status_t lStatus = in->set(format, channelCount, sampleRate, acoustics);
if (status) {
diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h
index d406424..bf63cc5 100644
--- a/libs/audioflinger/AudioHardwareStub.h
+++ b/libs/audioflinger/AudioHardwareStub.h
@@ -78,6 +78,7 @@
status_t *status=0);
virtual AudioStreamIn* openInputStream(
+ int inputSource,
int format,
int channelCount,
uint32_t sampleRate,
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index d0ff9ce..ac59799 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.WRITE_SDCARD" />
<application>
<uses-library android:name="android.test.runner" />
<activity android:label="@string/app_name"
diff --git a/media/tests/MediaFrameworkTest/res/raw/testmidi.mid b/media/tests/MediaFrameworkTest/res/raw/testmidi.mid
index df84e20..d4ead53 100644
--- a/media/tests/MediaFrameworkTest/res/raw/testmidi.mid
+++ b/media/tests/MediaFrameworkTest/res/raw/testmidi.mid
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3 b/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3
index 89c44b0..b7d69f8 100644
--- a/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3
+++ b/media/tests/MediaFrameworkTest/res/raw/testmp3.mp3
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 407b4b36..fd3a4ba 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -26,25 +26,24 @@
//A directory to hold all kinds of media files
public static final String MEDIA_SAMPLE_POOL = "/sdcard/media_api/samples/";
//Audio files
- public static final String MP3CBR = "/sdcard/media_api/music/MP3CBR.mp3";
- public static final String MP3VBR = "/sdcard/media_api/music/MP3VBR.mp3";
+ public static final String MP3CBR = "/sdcard/media_api/music/MP3_256kbps_2ch.mp3";
+ public static final String MP3VBR = "/sdcard/media_api/music/MP3_256kbps_2ch_VBR.mp3";
public static final String SHORTMP3 = "/sdcard/media_api/music/SHORTMP3.mp3";
- public static final String MIDI = "/sdcard/media_api/music/MIDI.mid";
+ public static final String MIDI = "/sdcard/media_api/music/ants.mid";
public static final String WMA9 = "/sdcard/media_api/music/WMA9.wma";
public static final String WMA10 = "/sdcard/media_api/music/WMA10.wma";
- public static final String WAV = "/sdcard/media_api/music/complicated_wav.wav";
- public static final String AMR = "/sdcard/media_api/music/AMRNB.amr";
- public static final String OGG = "/sdcard/media_api/music/Mists_of_Time-4T.ogg";
- public static final String OGGSHORT = "/sdcard/media_api/music/Skippy.ogg";
+ public static final String WAV = "/sdcard/media_api/music/rings_2ch.wav";
+ public static final String AMR = "/sdcard/media_api/music/test_amr_ietf.amr";
+ public static final String OGG = "/sdcard/media_api/music/Revelation.ogg";
- public static final int MP3CBR_LENGTH = 231116;
- public static final int MP3VBR_LENGTH = 126407;
+ public static final int MP3CBR_LENGTH = 71000;
+ public static final int MP3VBR_LENGTH = 71000;
public static final int SHORTMP3_LENGTH = 286;
- public static final int MIDI_LENGTH = 210528;
+ public static final int MIDI_LENGTH = 17000;
public static final int WMA9_LENGTH = 126559;
public static final int WMA10_LENGTH = 126559;
- public static final int AMR_LENGTH = 126540;
- public static final int OGG_LENGTH = 40000;
+ public static final int AMR_LENGTH = 37000;
+ public static final int OGG_LENGTH = 4000;
public static final int SEEK_TIME = 10000;
public static final long PAUSE_WAIT_TIME = 3000;
@@ -61,29 +60,21 @@
//public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp";
//local video
- public static final String VIDEO_MP4 = "/sdcard/media_api/video/gingerkids.MP4";
+ public static final String VIDEO_MP4 = "/sdcard/media_api/video/MPEG4_320_AAC_64.mp4";
public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp";
public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp";
public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp";
- public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_AAC.3gp";
- public static final String VIDEO_H263_AMR = "/sdcard/media_api/video/H263_AMR.3gp";
- public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_AAC.3gp";
- public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_AMR.3gp";
+ public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_56_AAC_24.3gp";
+ public static final String VIDEO_H263_AMR = "/sdcard/media_api/video/H263_56_AMRNB_6.3gp";
+ public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_320_AAC_64.3gp";
+ public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_320_AMRNB_6.3gp";
public static final String VIDEO_WMV = "/sdcard/media_api/video/bugs.wmv";
- public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/h263_qcif_30fps.3gp";
- public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/mpeg4_qvga_24fps.3gp";
+ public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
+ public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
//ringtone
public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3";
-
- //streaming mp3
- public static final String STREAM_LARGE_MP3 =
- "http://wms.pv.com:7070/MediaDownloadContent/mp3/BuenaVista_04_Pueblo_Nuevo.mp3";
- public static final String STREAM_SMALL_MP3 =
- "http://wms.pv.com:7070/MediaDownloadContent/mp3/ID3V2_TestFile.mp3";
- public static final String STREAM_REGULAR_MP3 =
- "http://wms.pv.com:7070/MediaDownloadContent/mp3/ElectricCosmo.mp3";
-
+
//streaming mp3
public static final String STREAM_MP3_1 =
"http://wms.pv.com:7070/MediaDownloadContent/mp3/chadthi_jawani_128kbps.mp3";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index cbd44ab..0c0974c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -32,8 +32,13 @@
import android.os.SystemClock;
import android.util.Log;
+import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.io.FileOutputStream;
import java.util.Random;
/**
* Junit / Instrumentation test case for the media player api
@@ -80,7 +85,9 @@
try{
mp.setDataSource(filePath);
mp.prepare();
- }catch (Exception e){}
+ }catch (Exception e){
+ Log.v(TAG, e.toString());
+ }
int duration = mp.getDuration();
Log.v(TAG, "Duration " + duration);
mp.release();
@@ -529,7 +536,6 @@
//Verify the thumbnail
Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);
-
outputWidth = outThumbnail.getWidth();
outputHeight = outThumbnail.getHeight();
goldenHeight = goldenBitmap.getHeight();
@@ -539,15 +545,18 @@
if ((outputWidth != goldenWidth) || (outputHeight != goldenHeight))
return false;
- //Check one line of pixel
- int x = goldenHeight/2;
- for (int j=0; j<goldenWidth; j++){
- if (goldenBitmap.getPixel(x, j) != outThumbnail.getPixel(x, j)){
+ // Check half line of pixel
+ int x = goldenHeight / 2;
+ for (int j = 1; j < goldenWidth / 2; j++) {
+ if (goldenBitmap.getPixel(x, j) != outThumbnail.getPixel(x, j)) {
Log.v(TAG, "pixel = " + goldenBitmap.getPixel(x, j));
- return false;
+ return false;
}
- }
- }catch (Exception e){}
+ }
+ }catch (Exception e){
+ Log.v(TAG, e.toString());
+ return false;
+ }
return true;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index 94c69a8..e01bd53 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -410,7 +410,7 @@
//Play a mid file which the duration is around 210 seconds
@LargeTest
public void testMidiResources() throws Exception {
- boolean midiResources = CodecTest.resourcesPlayback(MediaFrameworkTest.midiafd,180000);
+ boolean midiResources = CodecTest.resourcesPlayback(MediaFrameworkTest.midiafd,16000);
assertTrue("Play midi from resources", midiResources);
}
@@ -422,7 +422,7 @@
@MediumTest
public void testPrepareAsyncReset() throws Exception {
- boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_LARGE_MP3);
+ boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_MP3);
assertTrue("PrepareAsync Reset", isReset);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index b9d567c..f6958ed 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -65,7 +65,7 @@
private static final String TAG = "SettingsProvider";
private static final String DATABASE_NAME = "settings.db";
private static final int DATABASE_VERSION = 34;
-
+
private Context mContext;
public DatabaseHelper(Context context) {
@@ -81,7 +81,7 @@
");");
db.execSQL("CREATE INDEX secureIndex1 ON secure (name);");
}
-
+
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE system (" +
@@ -134,7 +134,7 @@
public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
Log.w(TAG, "Upgrading settings database from version " + oldVersion + " to "
+ currentVersion);
-
+
int upgradeVersion = oldVersion;
// Pattern for upgrade blocks:
@@ -143,7 +143,7 @@
// .. your upgrade logic..
// upgradeVersion = [the DATABASE_VERSION you set]
// }
-
+
if (upgradeVersion == 20) {
/*
* Version 21 is part of the volume control refresh. There is no
@@ -156,7 +156,7 @@
upgradeVersion = 21;
}
-
+
if (upgradeVersion < 22) {
upgradeVersion = 22;
// Upgrade the lock gesture storage location and format
@@ -186,7 +186,7 @@
}
upgradeVersion = 24;
}
-
+
if (upgradeVersion == 24) {
db.beginTransaction();
try {
@@ -213,7 +213,7 @@
}
upgradeVersion = 26;
}
-
+
if (upgradeVersion == 26) {
// This introduces the new secure settings table.
db.beginTransaction();
@@ -225,12 +225,12 @@
}
upgradeVersion = 27;
}
-
+
if (upgradeVersion == 27) {
// Copy settings values from 'system' to 'secure' and delete them from 'system'
SQLiteStatement insertStmt = null;
SQLiteStatement deleteStmt = null;
-
+
db.beginTransaction();
try {
insertStmt =
@@ -271,11 +271,11 @@
Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS,
Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS,
};
-
+
for (String setting : settingsToMove) {
insertStmt.bindString(1, setting);
insertStmt.execute();
-
+
deleteStmt.bindString(1, setting);
deleteStmt.execute();
}
@@ -291,7 +291,7 @@
}
upgradeVersion = 28;
}
-
+
if (upgradeVersion == 28 || upgradeVersion == 29) {
// Note: The upgrade to 28 was flawed since it didn't delete the old
// setting first before inserting. Combining 28 and 29 with the
@@ -313,10 +313,10 @@
} finally {
db.endTransaction();
}
-
+
upgradeVersion = 30;
}
-
+
if (upgradeVersion == 30) {
/*
* Upgrade 31 clears the title for all quick launch shortcuts so the
@@ -373,7 +373,7 @@
}
upgradeVersion = 33;
}
-
+
if (upgradeVersion == 33) {
// Set the default zoom controls to: tap-twice to bring up +/-
db.beginTransaction();
@@ -405,7 +405,7 @@
}
private void upgradeLockPatternLocation(SQLiteDatabase db) {
- Cursor c = db.query("system", new String[] {"_id", "value"}, "name='lock_pattern'",
+ Cursor c = db.query("system", new String[] {"_id", "value"}, "name='lock_pattern'",
null, null, null, null);
if (c.getCount() > 0) {
c.moveToFirst();
@@ -414,7 +414,7 @@
// Convert lock pattern
try {
LockPatternUtils lpu = new LockPatternUtils(mContext.getContentResolver());
- List<LockPatternView.Cell> cellPattern =
+ List<LockPatternView.Cell> cellPattern =
LockPatternUtils.stringToPattern(lockPattern);
lpu.saveLockPattern(cellPattern);
} catch (IllegalArgumentException e) {
@@ -542,12 +542,12 @@
AudioManager.RINGER_MODE_NORMAL);
loadVibrateSetting(db, false);
-
+
// By default, only the ring/notification and system streams are affected
loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
(1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |
(1 << AudioManager.STREAM_SYSTEM));
-
+
loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
((1 << AudioManager.STREAM_MUSIC) |
(1 << AudioManager.STREAM_RING) |
@@ -561,7 +561,7 @@
if (deleteOld) {
db.execSQL("DELETE FROM system WHERE name='" + Settings.System.VIBRATE_ON + "'");
}
-
+
SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ " VALUES(?,?);");
@@ -576,79 +576,78 @@
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);
- loadSecureSettings(db);
+ loadSecureSettings(db);
}
-
+
private void loadSystemSettings(SQLiteDatabase db) {
SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ " VALUES(?,?);");
-
+
Resources r = mContext.getResources();
- loadSetting(stmt, Settings.Secure.CURRENT_ACTIVE_PHONE,
- RILConstants.CDMA_PHONE);
+
loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
R.bool.def_dim_screen);
- loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
+ loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
"1".equals(SystemProperties.get("ro.kernel.qemu")) ? 1 : 0);
loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
R.integer.def_screen_off_timeout);
-
+
loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
R.bool.def_airplane_mode_on);
-
+
loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS,
R.string.def_airplane_mode_radios);
-
+
loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
R.bool.def_auto_time); // Sync time to NITZ
-
+
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
R.integer.def_screen_brightness);
-
+
loadDefaultAnimationSettings(stmt);
loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
R.bool.def_accelerometer_rotation);
-
+
// Default date format based on build
loadSetting(stmt, Settings.System.DATE_FORMAT,
- SystemProperties.get("ro.com.android.dateformat",
+ SystemProperties.get("ro.com.android.dateformat",
"MM-dd-yyyy"));
stmt.close();
}
-
+
private void loadDefaultAnimationSettings(SQLiteStatement stmt) {
loadFractionSetting(stmt, Settings.System.WINDOW_ANIMATION_SCALE,
R.fraction.def_window_animation_scale, 1);
loadFractionSetting(stmt, Settings.System.TRANSITION_ANIMATION_SCALE,
R.fraction.def_window_transition_scale, 1);
}
-
+
private void loadSecureSettings(SQLiteDatabase db) {
SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
-
+
loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
-
+
// Data roaming default, based on build
- loadSetting(stmt, Settings.Secure.DATA_ROAMING,
+ loadSetting(stmt, Settings.Secure.DATA_ROAMING,
"true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.dataroaming",
- "false")) ? 1 : 0);
-
+ SystemProperties.get("ro.com.android.dataroaming",
+ "false")) ? 1 : 0);
+
loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
R.bool.def_install_non_market_apps);
-
+
loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
R.string.def_location_providers_allowed);
-
+
loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
R.integer.def_network_preference);
-
+
loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED,
R.bool.def_usb_mass_storage_enabled);
-
+
loadBooleanSetting(stmt, Settings.Secure.WIFI_ON,
R.bool.def_wifi_on);
loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
@@ -660,25 +659,26 @@
}
// Set the preferred network mode to 0 = Global, CDMA default
- loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE,
+ int type = SystemProperties.getInt("ro.telephony.default_network",
RILConstants.PREFERRED_NETWORK_MODE);
+ loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
// Enable or disable Cell Broadcast SMS
loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS,
RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
// Set the preferred cdma subscription to 0 = Subscription from RUIM, when available
- loadSetting(stmt, Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
+ loadSetting(stmt, Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION,
RILConstants.PREFERRED_CDMA_SUBSCRIPTION);
// Don't do this. The SystemServer will initialize ADB_ENABLED from a
// persistent system property instead.
//loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0);
-
+
// Allow mock locations default, based on build
- loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION,
+ loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION,
"1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0);
-
+
stmt.close();
}
@@ -687,24 +687,23 @@
stmt.bindString(2, value.toString());
stmt.execute();
}
-
+
private void loadStringSetting(SQLiteStatement stmt, String key, int resid) {
loadSetting(stmt, key, mContext.getResources().getString(resid));
}
-
+
private void loadBooleanSetting(SQLiteStatement stmt, String key, int resid) {
loadSetting(stmt, key,
mContext.getResources().getBoolean(resid) ? "1" : "0");
}
-
+
private void loadIntegerSetting(SQLiteStatement stmt, String key, int resid) {
loadSetting(stmt, key,
Integer.toString(mContext.getResources().getInteger(resid)));
}
-
+
private void loadFractionSetting(SQLiteStatement stmt, String key, int resid, int base) {
loadSetting(stmt, key,
Float.toString(mContext.getResources().getFraction(resid, base, base)));
}
}
-
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c4dbd32..f2959e3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.AlertDialog;
+import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
@@ -41,6 +42,7 @@
import android.app.Instrumentation;
import android.app.PendingIntent;
import android.app.ResultInfo;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -78,10 +80,14 @@
import android.os.SystemProperties;
import android.provider.Checkin;
import android.provider.Settings;
+import android.server.data.CrashData;
+import android.server.data.StackTraceElementData;
+import android.server.data.ThrowableData;
import android.text.TextUtils;
import android.util.Config;
import android.util.EventLog;
import android.util.Log;
+import android.util.LogPrinter;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
import android.view.Gravity;
@@ -92,10 +98,13 @@
import dalvik.system.Zygote;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
@@ -191,6 +200,10 @@
// Maximum number of recent tasks that we can remember.
static final int MAX_RECENT_TASKS = 20;
+ // Amount of time after a call to stopAppSwitches() during which we will
+ // prevent further untrusted switches from happening.
+ static final long APP_SWITCH_DELAY_TIME = 5*1000;
+
// How long until we reset a task when the user returns to it. Currently
// 30 minutes.
static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
@@ -328,6 +341,21 @@
final ArrayList mHistory = new ArrayList();
/**
+ * Description of a request to start a new activity, which has been held
+ * due to app switches being disabled.
+ */
+ class PendingActivityLaunch {
+ HistoryRecord r;
+ HistoryRecord sourceRecord;
+ Uri[] grantedUriPermissions;
+ int grantedMode;
+ boolean onlyIfNeeded;
+ }
+
+ final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
+ = new ArrayList<PendingActivityLaunch>();
+
+ /**
* List of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
* contains broadcasts to registered receivers, to avoid spinning up
@@ -705,6 +733,18 @@
int mFactoryTest;
/**
+ * The time at which we will allow normal application switches again,
+ * after a call to {@link #stopAppSwitches()}.
+ */
+ long mAppSwitchesAllowedTime;
+
+ /**
+ * This is set to true after the first switch after mAppSwitchesAllowedTime
+ * is set; any switches after that will clear the time.
+ */
+ boolean mDidAppSwitch;
+
+ /**
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*/
@@ -852,6 +892,7 @@
static final int SERVICE_ERROR_MSG = 18;
static final int RESUME_TOP_ACTIVITY_MSG = 19;
static final int PROC_START_TIMEOUT_MSG = 20;
+ static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
AlertDialog mUidAlert;
@@ -910,6 +951,8 @@
d.show();
proc.anrDialog = d;
}
+
+ ensureScreenEnabled();
} break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
@@ -1041,6 +1084,11 @@
processStartTimedOutLocked(app);
}
}
+ case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
+ synchronized (ActivityManagerService.this) {
+ doPendingActivityLaunchesLocked(true);
+ }
+ }
}
}
};
@@ -1495,6 +1543,18 @@
return null;
}
+ private final HistoryRecord topRunningNonDelayedActivityLocked(HistoryRecord notTop) {
+ int i = mHistory.size()-1;
+ while (i >= 0) {
+ HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ if (!r.finishing && !r.delayedResume && r != notTop) {
+ return r;
+ }
+ i--;
+ }
+ return null;
+ }
+
/**
* This is a simplified version of topRunningActivityLocked that provides a number of
* optional skip-over modes. It is intended for use with the ActivityWatcher hook only.
@@ -2245,6 +2305,8 @@
return true;
}
+ next.delayedResume = false;
+
// If the top activity is the resumed one, nothing to do.
if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
// Make sure we have executed any pending transitions, since there
@@ -2471,7 +2533,8 @@
return true;
}
- private final void startActivityLocked(HistoryRecord r, boolean newTask) {
+ private final void startActivityLocked(HistoryRecord r, boolean newTask,
+ boolean doResume) {
final int NH = mHistory.size();
int addPos = -1;
@@ -2558,7 +2621,7 @@
if ((r.intent.getFlags()
&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
resetTaskIfNeededLocked(r, r);
- doShow = topRunningActivityLocked(null) == r;
+ doShow = topRunningNonDelayedActivityLocked(null) == r;
}
}
if (SHOW_APP_STARTING_ICON && doShow) {
@@ -2588,13 +2651,15 @@
mWindowManager.validateAppTokens(mHistory);
}
- resumeTopActivityLocked(null);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
}
/**
* Perform clear operation as requested by
- * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: assuming the top task on the
- * stack is the one that the new activity is being launched in, look for
+ * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the
+ * stack to the given task, then look for
* an instance of that activity in the stack and, if found, finish all
* activities on top of it and return the instance.
*
@@ -2602,9 +2667,21 @@
* @return Returns the old activity that should be continue to be used,
* or null if none was found.
*/
- private final HistoryRecord performClearTopTaskLocked(int taskId,
+ private final HistoryRecord performClearTaskLocked(int taskId,
HistoryRecord newR, boolean doClear) {
int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ HistoryRecord r = (HistoryRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
while (i > 0) {
i--;
HistoryRecord r = (HistoryRecord)mHistory.get(i);
@@ -2840,15 +2917,75 @@
intent, resolvedType, aInfo, mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
- HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
- != 0 ? r : null;
-
+ if (mResumedActivity == null
+ || mResumedActivity.info.applicationInfo.uid != callingUid) {
+ if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
+ PendingActivityLaunch pal = new PendingActivityLaunch();
+ pal.r = r;
+ pal.sourceRecord = sourceRecord;
+ pal.grantedUriPermissions = grantedUriPermissions;
+ pal.grantedMode = grantedMode;
+ pal.onlyIfNeeded = onlyIfNeeded;
+ mPendingActivityLaunches.add(pal);
+ return START_SWITCHES_CANCELED;
+ }
+ }
+
+ if (mDidAppSwitch) {
+ // This is the second allowed switch since we stopped switches,
+ // so now just generally allow switches. Use case: user presses
+ // home (switches disabled, switch to home, mDidAppSwitch now true);
+ // user taps a home icon (coming from home so allowed, we hit here
+ // and now allow anyone to switch again).
+ mAppSwitchesAllowedTime = 0;
+ } else {
+ mDidAppSwitch = true;
+ }
+
+ doPendingActivityLaunchesLocked(false);
+
+ return startActivityUncheckedLocked(r, sourceRecord,
+ grantedUriPermissions, grantedMode, onlyIfNeeded, true);
+ }
+
+ private final void doPendingActivityLaunchesLocked(boolean doResume) {
+ final int N = mPendingActivityLaunches.size();
+ if (N <= 0) {
+ return;
+ }
+ for (int i=0; i<N; i++) {
+ PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
+ startActivityUncheckedLocked(pal.r, pal.sourceRecord,
+ pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded,
+ doResume && i == (N-1));
+ }
+ mPendingActivityLaunches.clear();
+ }
+
+ private final int startActivityUncheckedLocked(HistoryRecord r,
+ HistoryRecord sourceRecord, Uri[] grantedUriPermissions,
+ int grantedMode, boolean onlyIfNeeded, boolean doResume) {
+ final Intent intent = r.intent;
+ final int callingUid = r.launchedFromUid;
+
+ int launchFlags = intent.getFlags();
+
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
if (DEBUG_USER_LEAVING) Log.v(TAG,
"startActivity() => mUserLeaving=" + mUserLeaving);
+ // If the caller has asked not to resume at this point, we make note
+ // of this in the record so that we can skip it when trying to find
+ // the top running activity.
+ if (!doResume) {
+ r.delayedResume = true;
+ }
+
+ HistoryRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
+ != 0 ? r : null;
+
// If the onlyIfNeeded flag is set, then we can do this if the activity
// being launched is the same as the one making the call... or, as
// a special case, if we do not know the caller then we count the
@@ -2856,7 +2993,7 @@
if (onlyIfNeeded) {
HistoryRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = topRunningActivityLocked(notTop);
+ checkedCaller = topRunningNonDelayedActivityLocked(notTop);
}
if (!checkedCaller.realActivity.equals(r.realActivity)) {
// Caller is not the same as launcher, so always needed.
@@ -2894,7 +3031,7 @@
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
- if (resultRecord != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// For whatever reason this activity is being launched into a new
// task... yet the caller has requested a result back. Well, that
// is pretty messed up, so instead immediately send back a cancel
@@ -2902,10 +3039,9 @@
// dependency on its originator.
Log.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
+ r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
r.resultTo = null;
- resultRecord = null;
}
boolean addingToTask = false;
@@ -2916,7 +3052,7 @@
// If bring to front is requested, and no result is requested, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
- if (resultRecord == null) {
+ if (r.resultTo == null) {
// See if there is a task to bring to the front. If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
@@ -2938,7 +3074,7 @@
// to have the same behavior as if a new instance was
// being started, which means not bringing it to the front
// if the caller is not itself in the front.
- HistoryRecord curTop = topRunningActivityLocked(notTop);
+ HistoryRecord curTop = topRunningNonDelayedActivityLocked(notTop);
if (curTop.task != taskTop.task) {
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
boolean callerAtFront = sourceRecord == null
@@ -2959,7 +3095,9 @@
// the client said not to do anything if that
// is the case, so this is it! And for paranoia, make
// sure we have correctly resumed the top activity.
- resumeTopActivityLocked(null);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
return START_RETURN_INTENT_TO_CALLER;
}
if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
@@ -2969,7 +3107,7 @@
// from the task up to the one being started. In most
// cases this means we are resetting the task to its
// initial state.
- HistoryRecord top = performClearTopTaskLocked(
+ HistoryRecord top = performClearTaskLocked(
taskTop.task.taskId, r, true);
if (top != null) {
if (top.frontOfTask) {
@@ -3035,7 +3173,9 @@
// We didn't do anything... but it was needed (a.k.a., client
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
- resumeTopActivityLocked(null);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
return START_TASK_TO_FRONT;
}
}
@@ -3052,8 +3192,8 @@
// If the activity being launched is the same as the one currently
// at the top, then we need to check if it should only be launched
// once.
- HistoryRecord top = topRunningActivityLocked(notTop);
- if (top != null && resultRecord == null) {
+ HistoryRecord top = topRunningNonDelayedActivityLocked(notTop);
+ if (top != null && r.resultTo == null) {
if (top.realActivity.equals(r.realActivity)) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
@@ -3062,7 +3202,9 @@
logStartActivity(LOG_AM_NEW_INTENT, top, top.task);
// For paranoia, make sure we have correctly
// resumed the top activity.
- resumeTopActivityLocked(null);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
if (onlyIfNeeded) {
// We don't need to start a new activity, and
// the client said not to do anything if that
@@ -3077,9 +3219,9 @@
}
} else {
- if (resultRecord != null) {
+ if (r.resultTo != null) {
sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
+ r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
}
return START_CLASS_NOT_FOUND;
@@ -3088,7 +3230,7 @@
boolean newTask = false;
// Should this be considered a new task?
- if (resultRecord == null && !addingToTask
+ if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// todo: should do better management of integers.
mCurTask++;
@@ -3108,14 +3250,16 @@
// In this case, we are adding the activity to an existing
// task, but the caller has asked to clear that task if the
// activity is already running.
- HistoryRecord top = performClearTopTaskLocked(
+ HistoryRecord top = performClearTaskLocked(
sourceRecord.task.taskId, r, true);
if (top != null) {
logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
deliverNewIntentLocked(top, r.intent);
// For paranoia, make sure we have correctly
// resumed the top activity.
- resumeTopActivityLocked(null);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
return START_DELIVERED_TO_TOP;
}
} else if (!addingToTask &&
@@ -3128,7 +3272,9 @@
HistoryRecord top = moveActivityToFrontLocked(where);
logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
deliverNewIntentLocked(top, r.intent);
- resumeTopActivityLocked(null);
+ if (doResume) {
+ resumeTopActivityLocked(null);
+ }
return START_DELIVERED_TO_TOP;
}
}
@@ -3157,7 +3303,7 @@
EventLog.writeEvent(LOG_AM_CREATE_TASK, r.task.taskId);
}
logStartActivity(LOG_AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask);
+ startActivityLocked(r, newTask, doResume);
return START_SUCCESS;
}
@@ -4911,6 +5057,20 @@
}
}
+ final void ensureScreenEnabled() {
+ boolean enableScreen;
+ synchronized (this) {
+ enableScreen = !mBooted;
+ mBooted = true;
+ }
+
+ if (enableScreen) {
+ EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
+ SystemClock.uptimeMillis());
+ enableScreenAfterBoot();
+ }
+ }
+
public final void activityPaused(IBinder token, Bundle icicle) {
// Refuse possible leaked file descriptors
if (icicle != null && icicle.hasFileDescriptors()) {
@@ -6251,6 +6411,10 @@
"moveTaskToFront()");
synchronized(this) {
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), "Task to front")) {
+ return;
+ }
final long origId = Binder.clearCallingIdentity();
try {
int N = mRecentTasks.size();
@@ -6335,6 +6499,12 @@
"moveTaskToBack()");
synchronized(this) {
+ if (mResumedActivity != null && mResumedActivity.task.taskId == task) {
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), "Task to back")) {
+ return;
+ }
+ }
final long origId = Binder.clearCallingIdentity();
moveTaskToBackLocked(task);
Binder.restoreCallingIdentity(origId);
@@ -6438,6 +6608,10 @@
"moveTaskBackwards()");
synchronized(this) {
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), "Task backwards")) {
+ return;
+ }
final long origId = Binder.clearCallingIdentity();
moveTaskBackwardsLocked(task);
Binder.restoreCallingIdentity(origId);
@@ -7179,6 +7353,55 @@
}
}
+ public void stopAppSwitches() {
+ if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.STOP_APP_SWITCHES);
+ }
+
+ synchronized(this) {
+ mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
+ + APP_SWITCH_DELAY_TIME;
+ mDidAppSwitch = false;
+ mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+ Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
+ mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
+ }
+ }
+
+ public void resumeAppSwitches() {
+ if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.STOP_APP_SWITCHES);
+ }
+
+ synchronized(this) {
+ // Note that we don't execute any pending app switches... we will
+ // let those wait until either the timeout, or the next start
+ // activity request.
+ mAppSwitchesAllowedTime = 0;
+ }
+ }
+
+ boolean checkAppSwitchAllowedLocked(int callingPid, int callingUid,
+ String name) {
+ if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
+ return true;
+ }
+
+ final int perm = checkComponentPermission(
+ android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
+ callingUid, -1);
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ Log.w(TAG, name + " request from " + callingUid + " stopped");
+ return false;
+ }
+
public void setDebugApp(String packageName, boolean waitForDebugger,
boolean persistent) {
enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
@@ -7595,6 +7818,30 @@
return handleAppCrashLocked(app);
}
+ private ComponentName getErrorReportReceiver(ProcessRecord app) {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ try {
+ // was an installer package name specified when this app was
+ // installed?
+ String installerPackageName = pm.getInstallerPackageName(app.info.packageName);
+ if (installerPackageName == null) {
+ return null;
+ }
+
+ // is there an Activity in this package that handles ACTION_APP_ERROR?
+ Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+ ResolveInfo info = pm.resolveIntentForPackage(intent, null, 0, installerPackageName);
+ if (info == null || info.activityInfo == null) {
+ return null;
+ }
+
+ return new ComponentName(installerPackageName, info.activityInfo.name);
+ } catch (RemoteException e) {
+ // will return null and no error report will be delivered
+ }
+ return null;
+ }
+
void makeAppNotRespondingLocked(ProcessRecord app,
String tag, String shortMsg, String longMsg, byte[] crashData) {
app.notResponding = true;
@@ -7713,6 +7960,7 @@
}
void startAppProblemLocked(ProcessRecord app) {
+ app.errorReportReceiver = getErrorReportReceiver(app);
skipCurrentReceiverLocked(app);
}
@@ -7745,7 +7993,6 @@
public int handleApplicationError(IBinder app, int flags,
String tag, String shortMsg, String longMsg, byte[] crashData) {
AppErrorResult result = new AppErrorResult();
-
ProcessRecord r = null;
synchronized (this) {
if (app != null) {
@@ -7834,16 +8081,96 @@
int res = result.get();
+ Intent appErrorIntent = null;
synchronized (this) {
if (r != null) {
mProcessCrashTimes.put(r.info.processName, r.info.uid,
SystemClock.uptimeMillis());
}
+ if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
+ appErrorIntent = createAppErrorIntentLocked(r);
+ res = AppErrorDialog.FORCE_QUIT;
+ }
+ }
+
+ if (appErrorIntent != null) {
+ try {
+ mContext.startActivity(appErrorIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "bug report receiver dissappeared", e);
+ }
}
return res;
}
+ Intent createAppErrorIntentLocked(ProcessRecord r) {
+ ApplicationErrorReport report = createAppErrorReportLocked(r);
+ if (report == null) {
+ return null;
+ }
+ Intent result = new Intent(Intent.ACTION_APP_ERROR);
+ result.setComponent(r.errorReportReceiver);
+ result.putExtra(Intent.EXTRA_BUG_REPORT, report);
+ result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return result;
+ }
+
+ ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) {
+ if (r.errorReportReceiver == null) {
+ return null;
+ }
+
+ if (!r.crashing && !r.notResponding) {
+ return null;
+ }
+
+ try {
+ ApplicationErrorReport report = new ApplicationErrorReport();
+ report.packageName = r.info.packageName;
+ report.installerPackageName = r.errorReportReceiver.getPackageName();
+ report.processName = r.processName;
+
+ if (r.crashing) {
+ report.type = ApplicationErrorReport.TYPE_CRASH;
+ report.crashInfo = new ApplicationErrorReport.CrashInfo();
+
+ ByteArrayInputStream byteStream = new ByteArrayInputStream(
+ r.crashingReport.crashData);
+ DataInputStream dataStream = new DataInputStream(byteStream);
+ CrashData crashData = new CrashData(dataStream);
+ ThrowableData throwData = crashData.getThrowableData();
+
+ report.time = crashData.getTime();
+ report.crashInfo.stackTrace = throwData.toString();
+
+ // extract the source of the exception, useful for report
+ // clustering
+ while (throwData.getCause() != null) {
+ throwData = throwData.getCause();
+ }
+ StackTraceElementData trace = throwData.getStackTrace()[0];
+ report.crashInfo.exceptionClassName = throwData.getType();
+ report.crashInfo.throwFileName = trace.getFileName();
+ report.crashInfo.throwClassName = trace.getClassName();
+ report.crashInfo.throwMethodName = trace.getMethodName();
+ } else if (r.notResponding) {
+ report.type = ApplicationErrorReport.TYPE_ANR;
+ report.anrInfo = new ApplicationErrorReport.AnrInfo();
+
+ report.anrInfo.activity = r.notRespondingReport.tag;
+ report.anrInfo.cause = r.notRespondingReport.shortMsg;
+ report.anrInfo.info = r.notRespondingReport.longMsg;
+ }
+
+ return report;
+ } catch (IOException e) {
+ // we don't send it
+ }
+
+ return null;
+ }
+
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
// assume our apps are happy - lazy create the list
List<ActivityManager.ProcessErrorStateInfo> errList = null;
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index 3fcfad0..33894d6 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -19,17 +19,22 @@
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
+import android.util.Log;
class AppErrorDialog extends BaseErrorDialog {
+ private final static String TAG = "AppErrorDialog";
+
private final AppErrorResult mResult;
private final ProcessRecord mProc;
// Event 'what' codes
static final int FORCE_QUIT = 0;
static final int DEBUG = 1;
+ static final int FORCE_QUIT_AND_REPORT = 2;
// 5-minute timeout, then we automatically dismiss the crash dialog
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -58,12 +63,22 @@
setCancelable(false);
- setButton(res.getText(com.android.internal.R.string.force_close),
- mHandler.obtainMessage(FORCE_QUIT));
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getText(com.android.internal.R.string.force_close),
+ mHandler.obtainMessage(FORCE_QUIT));
+
if ((flags&1) != 0) {
- setButton(res.getText(com.android.internal.R.string.debug),
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ res.getText(com.android.internal.R.string.debug),
mHandler.obtainMessage(DEBUG));
}
+
+ if (app.errorReportReceiver != null) {
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getText(com.android.internal.R.string.report),
+ mHandler.obtainMessage(FORCE_QUIT_AND_REPORT));
+ }
+
setTitle(res.getText(com.android.internal.R.string.aerr_title));
getWindow().addFlags(FLAG_SYSTEM_ERROR);
getWindow().setTitle("Application Error: " + app.info.processName);
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index 7390ed0..03c2a04 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -18,7 +18,10 @@
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
@@ -26,6 +29,13 @@
import android.util.Log;
class AppNotRespondingDialog extends BaseErrorDialog {
+ private static final String TAG = "AppNotRespondingDialog";
+
+ // Event 'what' codes
+ static final int FORCE_CLOSE = 1;
+ static final int WAIT = 2;
+ static final int WAIT_AND_REPORT = 3;
+
private final ActivityManagerService mService;
private final ProcessRecord mProc;
@@ -67,10 +77,19 @@
? res.getString(resid, name1.toString(), name2.toString())
: res.getString(resid, name1.toString()));
- setButton(res.getText(com.android.internal.R.string.force_close),
- mHandler.obtainMessage(1));
- setButton2(res.getText(com.android.internal.R.string.wait),
- mHandler.obtainMessage(2));
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getText(com.android.internal.R.string.force_close),
+ mHandler.obtainMessage(FORCE_CLOSE));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ res.getText(com.android.internal.R.string.wait),
+ mHandler.obtainMessage(WAIT));
+
+ if (app.errorReportReceiver != null) {
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getText(com.android.internal.R.string.report),
+ mHandler.obtainMessage(WAIT_AND_REPORT));
+ }
+
setTitle(res.getText(com.android.internal.R.string.anr_title));
getWindow().addFlags(FLAG_SYSTEM_ERROR);
getWindow().setTitle("Application Not Responding: " + app.info.processName);
@@ -81,16 +100,23 @@
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
+ Intent appErrorIntent = null;
switch (msg.what) {
- case 1:
+ case FORCE_CLOSE:
// Kill the application.
mService.killAppAtUsersRequest(mProc,
AppNotRespondingDialog.this, true);
break;
- case 2:
+ case WAIT_AND_REPORT:
+ case WAIT:
// Continue waiting for the application.
synchronized (mService) {
ProcessRecord app = mProc;
+
+ if (msg.what == WAIT_AND_REPORT) {
+ appErrorIntent = mService.createAppErrorIntentLocked(app);
+ }
+
app.notResponding = false;
app.notRespondingReport = null;
if (app.anrDialog == AppNotRespondingDialog.this) {
@@ -99,6 +125,14 @@
}
break;
}
+
+ if (appErrorIntent != null) {
+ try {
+ getContext().startActivity(appErrorIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "bug report receiver dissappeared", e);
+ }
+ }
}
};
}
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 1488791..1789687 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -85,6 +85,7 @@
boolean launchFailed; // set if a launched failed, to abort on 2nd try
boolean haveState; // have we gotten the last activity state?
boolean stopped; // is activity pause finished?
+ boolean delayedResume; // not yet resumed because of stopped app switches?
boolean finishing; // activity in pending finish list?
boolean configDestroy; // need to destroy due to config change?
int configChangeFlags; // which config values have changed
@@ -146,6 +147,7 @@
pw.print(" icicle="); pw.println(icicle);
pw.print(prefix); pw.print("state="); pw.print(state);
pw.print(" stopped="); pw.print(stopped);
+ pw.print(" delayedResume="); pw.print(delayedResume);
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
@@ -191,6 +193,7 @@
launchFailed = false;
haveState = false;
stopped = false;
+ delayedResume = false;
finishing = false;
configDestroy = false;
keysPaused = false;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 68aebc3..419dadf 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -107,6 +107,10 @@
ActivityManager.ProcessErrorStateInfo crashingReport;
ActivityManager.ProcessErrorStateInfo notRespondingReport;
+ // Who will be notified of the error. This is usually an activity in the
+ // app that installed the package.
+ ComponentName errorReportReceiver;
+
void dump(PrintWriter pw, String prefix) {
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
@@ -157,7 +161,14 @@
pw.print(" "); pw.print(crashDialog);
pw.print(" notResponding="); pw.print(notResponding);
pw.print(" " ); pw.print(anrDialog);
- pw.print(" bad="); pw.println(bad);
+ pw.print(" bad="); pw.print(bad);
+
+ // crashing or notResponding is always set before errorReportReceiver
+ if (errorReportReceiver != null) {
+ pw.print(" errorReportReceiver=");
+ pw.print(errorReportReceiver.flattenToShortString());
+ }
+ pw.println();
}
if (activities.size() > 0) {
pw.print(prefix); pw.print("activities="); pw.println(activities);
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 5336e27..48cbace 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -19,6 +19,7 @@
import com.android.internal.R;
import com.android.internal.util.CharSequences;
+import android.app.ActivityManagerNative;
import android.app.Dialog;
import android.app.IStatusBar;
import android.app.PendingIntent;
@@ -1254,6 +1255,14 @@
public void onClick(View v) {
try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ try {
mIntent.send();
mNotificationCallbacks.onNotificationClick(mPkg, mId);
} catch (PendingIntent.CanceledException e) {
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 1483522..7d600f0 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -62,13 +62,10 @@
* @hide
*/
public static CellLocation newFromBundle(Bundle bundle) {
- // TODO: My need to be use: Settings.Secure.getInt(mContext, Settings.Secure.CURRENT_ACTIVE_PHONE, 0))
- // instead of SystemProperties???
-
- // NOTE here TelephonyManager.getDefault().getPhoneType() cannot be used since at startup
- // ITelephony have not been created
- if (RILConstants.CDMA_PHONE == SystemProperties.getInt(
- Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.CDMA_PHONE)) {
+ // TelephonyManager.getDefault().getPhoneType() handles the case when
+ // ITelephony interface is not up yet.
+ int type = TelephonyManager.getDefault().getPhoneType();
+ if (type == RILConstants.CDMA_PHONE) {
return new CdmaCellLocation(bundle);
} else {
return new GsmCellLocation(bundle);
@@ -85,13 +82,10 @@
*
*/
public static CellLocation getEmpty() {
- // TODO: My need to be use: Settings.Secure.getInt(mContext, Settings.Secure.CURRENT_ACTIVE_PHONE, 0))
- // instead of SystemProperties???
-
- // NOTE here TelephonyManager.getDefault().getPhoneType() cannot be used since at startup
- // ITelephony have not been created
- if (RILConstants.CDMA_PHONE == SystemProperties.getInt(
- Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.CDMA_PHONE)) {
+ // TelephonyManager.getDefault().getPhoneType() handles the case when
+ // ITelephony interface is not up yet.
+ int type = TelephonyManager.getDefault().getPhoneType();
+ if (type == RILConstants.CDMA_PHONE) {
return new CdmaCellLocation();
} else {
return new GsmCellLocation();
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 3b7f4b5..e73de3c 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -20,6 +20,7 @@
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
@@ -307,7 +308,8 @@
if (PHONE_TYPE_CDMA == activePhone) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
- destinationAddress, message, statusReportRequested, header);
+ destinationAddress, message, statusReportRequested,
+ SmsHeader.fromByteArray(header));
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested, header);
@@ -331,7 +333,7 @@
if (PHONE_TYPE_CDMA == activePhone) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
- destinationAddress, message, statusReportRequested);
+ destinationAddress, message, statusReportRequested, null);
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 49a7750..a79eb3a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16,26 +16,24 @@
package android.telephony;
-import com.android.internal.telephony.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.telephony.CellLocation;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyProperties;
+import java.util.List;
+
/**
* Provides access to information about the telephony services on
* the device. Applications can use the methods in this class to
@@ -239,10 +237,10 @@
/**
* Returns the neighboring cell information of the device.
- *
+ *
* @return List of NeighboringCellInfo or null if info unavailable.
- *
- * <p>Requires Permission:
+ *
+ * <p>Requires Permission:
* (@link android.Manifest.permission#ACCESS_COARSE_UPDATES}
*/
public List<NeighboringCellInfo> getNeighboringCellInfo() {
@@ -251,24 +249,25 @@
} catch (RemoteException ex) {
}
return null;
-
+
}
-
+
/**
* No phone module
+ *
*/
public static final int PHONE_TYPE_NONE = 0;
/**
* GSM phone
*/
- public static final int PHONE_TYPE_GSM = 1;
+ public static final int PHONE_TYPE_GSM = RILConstants.GSM_PHONE;
/**
* CDMA phone
* @hide
*/
- public static final int PHONE_TYPE_CDMA = 2;
+ public static final int PHONE_TYPE_CDMA = RILConstants.CDMA_PHONE;
/**
* Returns a constant indicating the device phone type.
@@ -279,16 +278,41 @@
*/
public int getPhoneType() {
try{
- if(getITelephony().getActivePhoneType() == RILConstants.CDMA_PHONE) {
- return PHONE_TYPE_CDMA;
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ if(telephony.getActivePhoneType() == RILConstants.CDMA_PHONE) {
+ return PHONE_TYPE_CDMA;
+ } else {
+ return PHONE_TYPE_GSM;
+ }
} else {
- return PHONE_TYPE_GSM;
+ // This can happen when the ITelephony interface is not up yet.
+ return getPhoneTypeFromProperty();
}
- }catch(RemoteException ex){
- return PHONE_TYPE_NONE;
+ } catch(RemoteException ex){
+ // This shouldn't happen in the normal case, as a backup we
+ // read from the system property.
+ return getPhoneTypeFromProperty();
}
}
+
+ private int getPhoneTypeFromProperty() {
+ int type =
+ SystemProperties.getInt(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ getPhoneTypeFromNetworkType());
+ return type;
+ }
+
+ private int getPhoneTypeFromNetworkType() {
+ // When the system property CURRENT_ACTIVE_PHONE, has not been set,
+ // use the system property for default network type.
+ // This is a fail safe, and can only happen at first boot.
+ int mode = SystemProperties.getInt("ro.telephony.default_network", -1);
+ if (mode == -1)
+ return PHONE_TYPE_NONE;
+ return PhoneFactory.getPhoneType(mode);
+ }
//
//
// Current Network
@@ -640,8 +664,10 @@
/** Data connection activity: Currently both sending and receiving
* IP PPP traffic. */
public static final int DATA_ACTIVITY_INOUT = DATA_ACTIVITY_IN | DATA_ACTIVITY_OUT;
- /** Data connection is active, but physical link is down */
- /** @hide */
+ /**
+ * Data connection is active, but physical link is down
+ * @hide
+ */
public static final int DATA_ACTIVITY_DORMANT = 0x00000004;
/**
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 0928ddf..84dfca0 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -21,6 +21,7 @@
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
@@ -369,7 +370,8 @@
if (PHONE_TYPE_CDMA == activePhone) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
- destinationAddress, message, statusReportRequested, header);
+ destinationAddress, message, statusReportRequested,
+ SmsHeader.fromByteArray(header));
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested, header);
@@ -395,7 +397,7 @@
if (PHONE_TYPE_CDMA == activePhone) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
- destinationAddress, message, statusReportRequested);
+ destinationAddress, message, statusReportRequested, null);
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested);
@@ -744,4 +746,3 @@
}
}
}
-
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 8f4c69c..8e2941b 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -182,7 +182,7 @@
return stringToGsm7BitPacked(data);
}
- int headerBits = header.length * 8;
+ int headerBits = (header.length + 1) * 8;
int headerSeptets = headerBits / 7;
headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
@@ -194,7 +194,8 @@
(headerSeptets*7), true);
// Paste in the header
- System.arraycopy(header, 0, ret, 1, header.length);
+ ret[1] = (byte)header.length;
+ System.arraycopy(header, 0, ret, 2, header.length);
return ret;
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index b5f2afe..4d0cf41 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -165,18 +165,18 @@
// Used for preferred network type
// Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
- static final int NT_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */
- static final int NT_MODE_GSM_ONLY = 1; /* GSM only */
- static final int NT_MODE_WCDMA_ONLY = 2; /* WCDMA only */
- static final int NT_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL)
- AVAILABLE Application Settings menu */
- static final int NT_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL)
- AVAILABLE Application Settings menu */
- static final int NT_MODE_CDMA_NO_EVDO = 5; /* CDMA only */
- static final int NT_MODE_EVDO_NO_CDMA = 6; /* EvDo only */
- static final int NT_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according
- to PRL) AVAILABLE Application Settings menu */
- static final int PREFERRED_NT_MODE = NT_MODE_GLOBAL;
+ int NT_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
+ int NT_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
+ int NT_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+ int NT_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
+
+ int NT_MODE_CDMA = RILConstants.NETWORK_MODE_CDMA;
+
+ int NT_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+ int NT_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+ int NT_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
+
+ int PREFERRED_NT_MODE = RILConstants.PREFERRED_NETWORK_MODE;
// Used for CDMA roaming mode
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index 3beb60a..a84f74e 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -107,33 +107,49 @@
//reads the system properties and makes commandsinterface
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
- switch(networkMode) {
- case RILConstants.NETWORK_MODE_WCDMA_PREF:
- case RILConstants.NETWORK_MODE_GSM_ONLY:
- case RILConstants.NETWORK_MODE_WCDMA_ONLY:
- case RILConstants.NETWORK_MODE_GSM_UMTS:
- sProxyPhone = new PhoneProxy(new GSMPhone(context,
- sCommandsInterface, sPhoneNotifier));
- Log.i(LOG_TAG, "Creating GSMPhone");
- break;
- case RILConstants.NETWORK_MODE_CDMA:
- case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
- case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
- sProxyPhone = new PhoneProxy(new CDMAPhone(context,
- sCommandsInterface, sPhoneNotifier));
- Log.i(LOG_TAG, "Creating CDMAPhone");
- break;
- case RILConstants.NETWORK_MODE_GLOBAL:
- default:
- sProxyPhone = new PhoneProxy(new CDMAPhone(context,
- sCommandsInterface, sPhoneNotifier));
- Log.i(LOG_TAG, "Creating CDMAPhone");
+ int phoneType = getPhoneType(networkMode);
+ if (phoneType == RILConstants.GSM_PHONE) {
+ sProxyPhone = new PhoneProxy(new GSMPhone(context,
+ sCommandsInterface, sPhoneNotifier));
+ Log.i(LOG_TAG, "Creating GSMPhone");
+ } else if (phoneType == RILConstants.CDMA_PHONE) {
+ sProxyPhone = new PhoneProxy(new CDMAPhone(context,
+ sCommandsInterface, sPhoneNotifier));
+ Log.i(LOG_TAG, "Creating CDMAPhone");
}
+
sMadeDefaults = true;
}
}
}
+ /*
+ * This function returns the type of the phone, depending
+ * on the network mode.
+ *
+ * @param network mode
+ * @return Phone Type
+ */
+ public static int getPhoneType(int networkMode) {
+ switch(networkMode) {
+ case RILConstants.NETWORK_MODE_CDMA:
+ case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+ case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+ return RILConstants.CDMA_PHONE;
+
+ case RILConstants.NETWORK_MODE_WCDMA_PREF:
+ case RILConstants.NETWORK_MODE_GSM_ONLY:
+ case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+ case RILConstants.NETWORK_MODE_GSM_UMTS:
+ return RILConstants.GSM_PHONE;
+
+ case RILConstants.NETWORK_MODE_GLOBAL:
+ return RILConstants.CDMA_PHONE;
+ default:
+ return RILConstants.GSM_PHONE;
+ }
+ }
+
public static Phone getDefaultPhone() {
if (sLooper != Looper.myLooper()) {
throw new RuntimeException(
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 04e33fe..070d233 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1857,7 +1857,7 @@
* and/or radio knowing.
*/
if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF");
- setRadioPower(false, null);
+ setRadioPower(false, null);
} else {
if (DBG) Log.d(LOG_TAG, "Radio OFF @ init");
setRadioState(newState);
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index f2bd361..d055c311 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -122,7 +122,7 @@
* CONCATENATED_16_BIT_REFERENCE message set. Should be
* incremented for each set of concatenated messages.
*/
- protected static int sConcatenatedRef;
+ private static int sConcatenatedRef;
private SmsCounter mCounter;
@@ -132,6 +132,11 @@
private static SmsMessageBase mSmsMessageBase;
private SmsMessageBase.SubmitPduBase mSubmitPduBase;
+ protected static int getNextConcatenatedRef() {
+ sConcatenatedRef += 1;
+ return sConcatenatedRef;
+ }
+
/**
* Implement the per-application based SMS control, which only allows
* a limit on the number of SMS/MMS messages an app can send in checking
@@ -419,12 +424,15 @@
/**
* If this is the last part send the parts out to the application, otherwise
* the part is stored for later processing.
+ *
+ * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
*/
- protected void processMessagePart(SmsMessageBase sms, int referenceNumber,
- int sequence, int count, int destinationPort) {
+ protected void processMessagePart(SmsMessageBase sms,
+ SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
+
// Lookup all other related parts
StringBuilder where = new StringBuilder("reference_number =");
- where.append(referenceNumber);
+ where.append(concatRef.refNumber);
where.append(" AND address = ?");
String[] whereArgs = new String[] {sms.getOriginatingAddress()};
@@ -433,20 +441,19 @@
try {
cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null);
int cursorCount = cursor.getCount();
- if (cursorCount != count - 1) {
+ if (cursorCount != concatRef.msgCount - 1) {
// We don't have all the parts yet, store this one away
ContentValues values = new ContentValues();
values.put("date", new Long(sms.getTimestampMillis()));
values.put("pdu", HexDump.toHexString(sms.getPdu()));
values.put("address", sms.getOriginatingAddress());
- values.put("reference_number", referenceNumber);
- values.put("count", count);
- values.put("sequence", sequence);
- if (destinationPort != -1) {
- values.put("destination_port", destinationPort);
+ values.put("reference_number", concatRef.refNumber);
+ values.put("count", concatRef.msgCount);
+ values.put("sequence", concatRef.seqNumber);
+ if (portAddrs != null) {
+ values.put("destination_port", portAddrs.destPort);
}
mResolver.insert(mRawUri, values);
-
return;
}
@@ -454,7 +461,7 @@
int pduColumn = cursor.getColumnIndex("pdu");
int sequenceColumn = cursor.getColumnIndex("sequence");
- pdus = new byte[count][];
+ pdus = new byte[concatRef.msgCount][];
for (int i = 0; i < cursorCount; i++) {
cursor.moveToNext();
int cursorSequence = (int)cursor.getLong(sequenceColumn);
@@ -462,7 +469,7 @@
cursor.getString(pduColumn));
}
// This one isn't in the DB, so add it
- pdus[sequence - 1] = sms.getPdu();
+ pdus[concatRef.seqNumber - 1] = sms.getPdu();
// Remove the parts from the database
mResolver.delete(mRawUri, where.toString(), whereArgs);
@@ -473,31 +480,34 @@
if (cursor != null) cursor.close();
}
+ /**
+ * TODO(cleanup): The following code has duplicated logic with
+ * the radio-specific dispatchMessage code, which is fragile,
+ * in addition to being redundant. Instead, if this method
+ * maybe returned the reassembled message (or just contents),
+ * the following code (which is not really related to
+ * reconstruction) could be better consolidated.
+ */
+
// Dispatch the PDUs to applications
- switch (destinationPort) {
- case SmsHeader.PORT_WAP_PUSH: {
- // Build up the data stream
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- for (int i = 0; i < count; i++) {
- SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
- byte[] data = msg.getUserData();
- output.write(data, 0, data.length);
+ if (portAddrs != null) {
+ if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
+ // Build up the data stream
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ for (int i = 0; i < concatRef.msgCount; i++) {
+ SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
+ byte[] data = msg.getUserData();
+ output.write(data, 0, data.length);
+ }
+ // Handle the PUSH
+ mWapPush.dispatchWapPdu(output.toByteArray());
+ } else {
+ // The messages were sent to a port, so concoct a URI for it
+ dispatchPortAddressedPdus(pdus, portAddrs.destPort);
}
-
- // Handle the PUSH
- mWapPush.dispatchWapPdu(output.toByteArray());
- break;
- }
-
- case -1:
+ } else {
// The messages were not sent to a port
dispatchPdus(pdus);
- break;
-
- default:
- // The messages were sent to a port, so concoct a URI for it
- dispatchPortAddressedPdus(pdus, destinationPort);
- break;
}
}
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index 64b884e..d220648 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -16,227 +16,233 @@
package com.android.internal.telephony;
+import android.telephony.SmsMessage;
+
import com.android.internal.util.HexDump;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
import java.util.ArrayList;
/**
- * This class represents a SMS user data header.
- *
+ * SMS user data header, as specified in TS 23.040 9.2.3.24.
*/
public class SmsHeader {
- /** See TS 23.040 9.2.3.24 for description of this element ID. */
- public static final int CONCATENATED_8_BIT_REFERENCE = 0x00;
- /** See TS 23.040 9.2.3.24 for description of this element ID. */
- public static final int SPECIAL_SMS_MESSAGE_INDICATION = 0x01;
- /** See TS 23.040 9.2.3.24 for description of this element ID. */
- public static final int APPLICATION_PORT_ADDRESSING_8_BIT = 0x04;
- /** See TS 23.040 9.2.3.24 for description of this element ID. */
- public static final int APPLICATION_PORT_ADDRESSING_16_BIT= 0x05;
- /** See TS 23.040 9.2.3.24 for description of this element ID. */
- public static final int CONCATENATED_16_BIT_REFERENCE = 0x08;
+
+ // TODO(cleanup): this datastructure is generally referred to as
+ // the 'user data header' or UDH, and so the class name should
+ // change to reflect this...
+
+ /** SMS user data header information element identifiers.
+ * (see TS 23.040 9.2.3.24)
+ */
+ public static final int ELT_ID_CONCATENATED_8_BIT_REFERENCE = 0x00;
+ public static final int ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION = 0x01;
+ public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT = 0x04;
+ public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT = 0x05;
+ public static final int ELT_ID_SMSC_CONTROL_PARAMS = 0x06;
+ public static final int ELT_ID_UDH_SOURCE_INDICATION = 0x07;
+ public static final int ELT_ID_CONCATENATED_16_BIT_REFERENCE = 0x08;
+ public static final int ELT_ID_WIRELESS_CTRL_MSG_PROTOCOL = 0x09;
+ public static final int ELT_ID_TEXT_FORMATTING = 0x0A;
+ public static final int ELT_ID_PREDEFINED_SOUND = 0x0B;
+ public static final int ELT_ID_USER_DEFINED_SOUND = 0x0C;
+ public static final int ELT_ID_PREDEFINED_ANIMATION = 0x0D;
+ public static final int ELT_ID_LARGE_ANIMATION = 0x0E;
+ public static final int ELT_ID_SMALL_ANIMATION = 0x0F;
+ public static final int ELT_ID_LARGE_PICTURE = 0x10;
+ public static final int ELT_ID_SMALL_PICTURE = 0x11;
+ public static final int ELT_ID_VARIABLE_PICTURE = 0x12;
+ public static final int ELT_ID_USER_PROMPT_INDICATOR = 0x13;
+ public static final int ELT_ID_EXTENDED_OBJECT = 0x14;
+ public static final int ELT_ID_REUSED_EXTENDED_OBJECT = 0x15;
+ public static final int ELT_ID_COMPRESSION_CONTROL = 0x16;
+ public static final int ELT_ID_OBJECT_DISTR_INDICATOR = 0x17;
+ public static final int ELT_ID_STANDARD_WVG_OBJECT = 0x18;
+ public static final int ELT_ID_CHARACTER_SIZE_WVG_OBJECT = 0x19;
+ public static final int ELT_ID_EXTENDED_OBJECT_DATA_REQUEST_CMD = 0x1A;
+ public static final int ELT_ID_RFC_822_EMAIL_HEADER = 0x20;
+ public static final int ELT_ID_HYPERLINK_FORMAT_ELEMENT = 0x21;
+ public static final int ELT_ID_REPLY_ADDRESS_ELEMENT = 0x22;
+ public static final int ELT_ID_ENHANCED_VOICE_MAIL_INFORMATION = 0x23;
public static final int PORT_WAP_PUSH = 2948;
- public static final int PORT_WAP_WSP = 9200;
+ public static final int PORT_WAP_WSP = 9200;
- private byte[] m_data;
- private ArrayList<Element> m_elements = new ArrayList<Element>();
- public int nbrOfHeaders;
+ public static class PortAddrs {
+ public int destPort;
+ public int origPort;
+ public boolean areEightBits;
+ }
+
+ public static class ConcatRef {
+ public int refNumber;
+ public int seqNumber;
+ public int msgCount;
+ public boolean isEightBits;
+ }
/**
- * Creates an SmsHeader object from raw user data header bytes.
- *
- * @param data is user data header bytes
- * @return an SmsHeader object
+ * A header element that is not explicitly parsed, meaning not
+ * PortAddrs or ConcatRef.
*/
- public static SmsHeader parse(byte[] data) {
- SmsHeader header = new SmsHeader();
- header.m_data = data;
+ public static class MiscElt {
+ public int id;
+ public byte[] data;
+ }
- int index = 0;
- header.nbrOfHeaders = 0;
- while (index < data.length) {
- int id = data[index++] & 0xff;
- int length = data[index++] & 0xff;
- byte[] elementData = new byte[length];
- System.arraycopy(data, index, elementData, 0, length);
- header.add(new Element(id, elementData));
- index += length;
- header.nbrOfHeaders++;
+ public PortAddrs portAddrs;
+ public ConcatRef concatRef;
+ public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
+
+ public SmsHeader() {}
+
+ /**
+ * Create structured SmsHeader object from serialized byte array representation.
+ * (see TS 23.040 9.2.3.24)
+ * @param data is user data header bytes
+ * @return SmsHeader object
+ */
+ public static SmsHeader fromByteArray(byte[] data) {
+ ByteArrayInputStream inStream = new ByteArrayInputStream(data);
+ SmsHeader smsHeader = new SmsHeader();
+ while (inStream.available() > 0) {
+ /**
+ * NOTE: as defined in the spec, ConcatRef and PortAddr
+ * fields should not reoccur, but if they do the last
+ * occurrence is to be used.
+ */
+ int id = inStream.read();
+ int length = inStream.read();
+ ConcatRef concatRef;
+ PortAddrs portAddrs;
+ switch (id) {
+ case ELT_ID_CONCATENATED_8_BIT_REFERENCE:
+ concatRef = new ConcatRef();
+ concatRef.refNumber = inStream.read();
+ concatRef.msgCount = inStream.read();
+ concatRef.seqNumber = inStream.read();
+ concatRef.isEightBits = true;
+ smsHeader.concatRef = concatRef;
+ break;
+ case ELT_ID_CONCATENATED_16_BIT_REFERENCE:
+ concatRef = new ConcatRef();
+ concatRef.refNumber = (inStream.read() << 8) | inStream.read();
+ concatRef.msgCount = inStream.read();
+ concatRef.seqNumber = inStream.read();
+ concatRef.isEightBits = false;
+ smsHeader.concatRef = concatRef;
+ break;
+ case ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT:
+ portAddrs = new PortAddrs();
+ portAddrs.destPort = inStream.read();
+ portAddrs.origPort = inStream.read();
+ portAddrs.areEightBits = true;
+ smsHeader.portAddrs = portAddrs;
+ break;
+ case ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT:
+ portAddrs = new PortAddrs();
+ portAddrs.destPort = (inStream.read() << 8) | inStream.read();
+ portAddrs.origPort = (inStream.read() << 8) | inStream.read();
+ portAddrs.areEightBits = false;
+ smsHeader.portAddrs = portAddrs;
+ break;
+ default:
+ MiscElt miscElt = new MiscElt();
+ miscElt.id = id;
+ miscElt.data = new byte[length];
+ inStream.read(miscElt.data, 0, length);
+ smsHeader.miscEltList.add(miscElt);
+ }
+ }
+ return smsHeader;
+ }
+
+ /**
+ * Create serialized byte array representation from structured SmsHeader object.
+ * (see TS 23.040 9.2.3.24)
+ * @return Byte array representing the SmsHeader
+ */
+ public static byte[] toByteArray(SmsHeader smsHeader) {
+ if ((smsHeader.portAddrs == null) &&
+ (smsHeader.concatRef == null) &&
+ (smsHeader.miscEltList.size() == 0)) {
+ return null;
}
- return header;
- }
-
- public SmsHeader() { }
-
- /**
- * Returns the list of SmsHeader Elements that make up the header.
- *
- * @return the list of SmsHeader Elements.
- */
- public ArrayList<Element> getElements() {
- return m_elements;
- }
-
- /**
- * Add an element to the SmsHeader.
- *
- * @param element to add.
- */
- public void add(Element element) {
- m_elements.add(element);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream(SmsMessage.MAX_USER_DATA_BYTES);
+ ConcatRef concatRef = smsHeader.concatRef;
+ if (concatRef != null) {
+ if (concatRef.isEightBits) {
+ outStream.write(ELT_ID_CONCATENATED_8_BIT_REFERENCE);
+ outStream.write(3);
+ outStream.write(concatRef.refNumber);
+ } else {
+ outStream.write(ELT_ID_CONCATENATED_16_BIT_REFERENCE);
+ outStream.write(4);
+ outStream.write(concatRef.refNumber >>> 8);
+ outStream.write(concatRef.refNumber & 0x00FF);
+ }
+ outStream.write(concatRef.msgCount);
+ outStream.write(concatRef.seqNumber);
+ }
+ PortAddrs portAddrs = smsHeader.portAddrs;
+ if (portAddrs != null) {
+ if (portAddrs.areEightBits) {
+ outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT);
+ outStream.write(2);
+ outStream.write(portAddrs.destPort);
+ outStream.write(portAddrs.origPort);
+ } else {
+ outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT);
+ outStream.write(4);
+ outStream.write(portAddrs.destPort >>> 8);
+ outStream.write(portAddrs.destPort & 0x00FF);
+ outStream.write(portAddrs.origPort >>> 8);
+ outStream.write(portAddrs.origPort & 0x00FF);
+ }
+ }
+ for (MiscElt miscElt : smsHeader.miscEltList) {
+ outStream.write(miscElt.id);
+ outStream.write(miscElt.data.length);
+ outStream.write(miscElt.data, 0, miscElt.data.length);
+ }
+ return outStream.toByteArray();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
-
- builder.append("UDH LENGTH: " + m_data.length + " octets");
- builder.append("UDH: ");
- builder.append(HexDump.toHexString(m_data));
- builder.append("\n");
-
- for (Element e : getElements()) {
- builder.append(" 0x" + HexDump.toHexString((byte)e.getID()) + " - ");
- switch (e.getID()) {
- case CONCATENATED_8_BIT_REFERENCE: {
- builder.append("Concatenated Short Message 8bit ref\n");
- byte[] data = e.getData();
- builder.append(" " + data.length + " (0x");
- builder.append(HexDump.toHexString((byte)data.length)
- + ") Bytes - Information Element\n");
- builder.append(" " + data[0] + " : SM reference number\n");
- builder.append(" " + data[1] + " : number of messages\n");
- builder.append(" " + data[2] + " : this SM sequence number\n");
- break;
- }
-
- case CONCATENATED_16_BIT_REFERENCE: {
- builder.append("Concatenated Short Message 16bit ref\n");
- byte[] data = e.getData();
- builder.append(" " + data.length + " (0x");
- builder.append(HexDump.toHexString((byte)data.length)
- + ") Bytes - Information Element\n");
- builder.append(" " + (data[0] & 0xff) * 256 + (data[1] & 0xff)
- + " : SM reference number\n");
- builder.append(" " + data[2] + " : number of messages\n");
- builder.append(" " + data[3] + " : this SM sequence number\n");
- break;
- }
-
- case APPLICATION_PORT_ADDRESSING_8_BIT:
- {
- builder.append("Application port addressing 8bit\n");
- byte[] data = e.getData();
-
- builder.append(" " + data.length + " (0x");
- builder.append(HexDump.toHexString(
- (byte)data.length) + ") Bytes - Information Element\n");
-
- int source = (data[0] & 0xff);
- builder.append(" " + source + " : DESTINATION port\n");
-
- int dest = (data[1] & 0xff);
- builder.append(" " + dest + " : SOURCE port\n");
- break;
- }
-
- case APPLICATION_PORT_ADDRESSING_16_BIT: {
- builder.append("Application port addressing 16bit\n");
- byte[] data = e.getData();
-
- builder.append(" " + data.length + " (0x");
- builder.append(HexDump.toHexString((byte)data.length)
- + ") Bytes - Information Element\n");
-
- int source = (data[0] & 0xff) << 8;
- source |= (data[1] & 0xff);
- builder.append(" " + source + " : DESTINATION port\n");
-
- int dest = (data[2] & 0xff) << 8;
- dest |= (data[3] & 0xff);
- builder.append(" " + dest + " : SOURCE port\n");
- break;
- }
-
- default: {
- builder.append("Unknown element\n");
- break;
- }
- }
+ builder.append("UserDataHeader ");
+ builder.append("{ ConcatRef ");
+ if (concatRef == null) {
+ builder.append("unset");
+ } else {
+ builder.append("{ refNumber=" + concatRef.refNumber);
+ builder.append(", msgCount=" + concatRef.msgCount);
+ builder.append(", seqNumber=" + concatRef.seqNumber);
+ builder.append(", isEightBits=" + concatRef.isEightBits);
+ builder.append(" }");
}
-
+ builder.append(", PortAddrs ");
+ if (portAddrs == null) {
+ builder.append("unset");
+ } else {
+ builder.append("{ destPort=" + portAddrs.destPort);
+ builder.append(", origPort=" + portAddrs.origPort);
+ builder.append(", areEightBits=" + portAddrs.areEightBits);
+ builder.append(" }");
+ }
+ for (MiscElt miscElt : miscEltList) {
+ builder.append(", MiscElt ");
+ builder.append("{ id=" + miscElt.id);
+ builder.append(", length=" + miscElt.data.length);
+ builder.append(", data=" + HexDump.toHexString(miscElt.data));
+ builder.append(" }");
+ }
+ builder.append(" }");
return builder.toString();
}
- private int calcSize() {
- int size = 1; // +1 for the UDHL field
- for (Element e : m_elements) {
- size += e.getData().length;
- size += 2; // 1 byte ID, 1 byte length
- }
-
- return size;
- }
-
- /**
- * Converts SmsHeader object to a byte array as specified in TS 23.040 9.2.3.24.
- * @return Byte array representing the SmsHeader
- */
- public byte[] toByteArray() {
- if (m_elements.size() == 0) return null;
-
- if (m_data == null) {
- int size = calcSize();
- int cur = 1;
- m_data = new byte[size];
-
- m_data[0] = (byte) (size-1); // UDHL does not include itself
-
- for (Element e : m_elements) {
- int length = e.getData().length;
- m_data[cur++] = (byte) e.getID();
- m_data[cur++] = (byte) length;
- System.arraycopy(e.getData(), 0, m_data, cur, length);
- cur += length;
- }
- }
-
- return m_data;
- }
-
- /**
- * A single Element in the SMS User Data Header.
- *
- * See TS 23.040 9.2.3.24.
- *
- */
- public static class Element {
- private byte[] m_data;
- private int m_id;
-
- public Element(int id, byte[] data) {
- m_id = id;
- m_data = data;
- }
-
- /**
- * Returns the Information Element Identifier for this element.
- *
- * @return the IE identifier.
- */
- public int getID() {
- return m_id;
- }
-
- /**
- * Returns the data portion of this element.
- *
- * @return element data.
- */
- public byte[] getData() {
- return m_data;
- }
- }
}
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 1aad38d..31bb652 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -245,8 +245,6 @@
/**
* Returns an object representing the user data header
*
- * @return an object representing the user data header
- *
* {@hide}
*/
public SmsHeader getUserDataHeader() {
@@ -254,9 +252,14 @@
}
/**
+ * TODO(cleanup): The term PDU is used in a seemingly non-unique
+ * manner -- for example, what is the difference between this byte
+ * array and the contents of SubmitPdu objects. Maybe a more
+ * illustrative term would be appropriate.
+ */
+
+ /**
* Returns the raw PDU for the message.
- *
- * @return the raw PDU for the message.
*/
public byte[] getPdu() {
return mPdu;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 67ae169..453185f 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -69,6 +69,8 @@
*/
static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country";
+ static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type";
+
//****** SIM Card
/**
* One of <code>"UNKNOWN"</code> <code>"ABSENT"</code> <code>"PIN_REQUIRED"</code>
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 3f8d40c..03f7f98 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -52,6 +52,7 @@
import com.android.internal.telephony.PhoneSubInfo;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.ArrayList;
import java.util.List;
@@ -126,8 +127,8 @@
//Change the system setting
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.CDMA_PHONE);
+ SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ new Integer(RILConstants.CDMA_PHONE).toString());
}
public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 42c0583..2bb17e4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -29,7 +29,6 @@
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SMSDispatcher;
-//import com.android.internal.telephony.SMSDispatcher.SmsTracker;
import com.android.internal.telephony.cdma.SmsMessage;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.util.HexDump;
@@ -42,8 +41,11 @@
final class CdmaSMSDispatcher extends SMSDispatcher {
private static final String TAG = "CDMA";
+ private CDMAPhone mCdmaPhone;
+
CdmaSMSDispatcher(CDMAPhone phone) {
super(phone);
+ mCdmaPhone = phone;
}
/**
@@ -70,50 +72,39 @@
if (smsb == null) {
return;
}
- SmsMessage sms = (SmsMessage) smsb;
- int teleService;
- boolean handled = false;
// Decode BD stream and set sms variables.
+ SmsMessage sms = (SmsMessage) smsb;
sms.parseSms();
- teleService = sms.getTeleService();
+ int teleService = sms.getTeleService();
+ boolean handled = false;
// Teleservices W(E)MT and VMN are handled together:
- if ((SmsEnvelope.TELESERVICE_WMT == teleService)
- ||(SmsEnvelope.TELESERVICE_WEMT == teleService)
- ||(SmsEnvelope.TELESERVICE_VMN == teleService)){
+ if ((teleService == SmsEnvelope.TELESERVICE_WMT)
+ || (teleService == SmsEnvelope.TELESERVICE_WEMT)
+ || (teleService == SmsEnvelope.TELESERVICE_VMN)) {
// From here on we need decoded BD.
// Special case the message waiting indicator messages
if (sms.isMWISetMessage()) {
- ((CDMAPhone) mPhone).updateMessageWaitingIndicator(true);
-
- if (sms.isMwiDontStore()) {
- handled = true;
- }
-
+ mCdmaPhone.updateMessageWaitingIndicator(true);
+ handled |= sms.isMwiDontStore();
if (Config.LOGD) {
- Log.d(TAG,
- "Received voice mail indicator set SMS shouldStore=" + !handled);
+ Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
}
} else if (sms.isMWIClearMessage()) {
- ((CDMAPhone) mPhone).updateMessageWaitingIndicator(false);
-
- if (sms.isMwiDontStore()) {
- handled = true;
- }
-
+ mCdmaPhone.updateMessageWaitingIndicator(false);
+ handled |= sms.isMwiDontStore();
if (Config.LOGD) {
- Log.d(TAG,
- "Received voice mail indicator clear SMS shouldStore=" + !handled);
+ Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
}
}
}
- if (null == sms.getUserData()){
- handled = true;
+ if (sms.getUserData() == null) {
if (Config.LOGD) {
Log.d(TAG, "Received SMS without user data");
}
+ handled = true;
}
if (handled) return;
@@ -123,82 +114,44 @@
return;
}
- // Parse the headers to see if this is partial, or port addressed
- int referenceNumber = -1;
- int count = 0;
- int sequence = 0;
- int destPort = -1;
- // From here on we need BD distributed to SMS member variables.
+ /**
+ * TODO(cleanup): Why are we using a getter method for this
+ * (and for so many other sms fields)? Trivial getters and
+ * setters like this are direct violations of the style guide.
+ * If the purpose is to protect agaist writes (by not
+ * providing a setter) then any protection is illusory (and
+ * hence bad) for cases where the values are not primitives,
+ * such as this call for the header. Since this is an issue
+ * with the public API it cannot be changed easily, but maybe
+ * something can be done eventually.
+ */
+ SmsHeader smsHeader = sms.getUserDataHeader();
- SmsHeader header = sms.getUserDataHeader();
- if (header != null) {
- for (SmsHeader.Element element : header.getElements()) {
- try {
- switch (element.getID()) {
- case SmsHeader.CONCATENATED_8_BIT_REFERENCE: {
- byte[] data = element.getData();
-
- referenceNumber = data[0] & 0xff;
- count = data[1] & 0xff;
- sequence = data[2] & 0xff;
-
- // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence
- // is zero, or sequence > count, ignore the entire element
- if (count == 0 || sequence == 0 || sequence > count) {
- referenceNumber = -1;
- }
- break;
- }
-
- case SmsHeader.CONCATENATED_16_BIT_REFERENCE: {
- byte[] data = element.getData();
-
- referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff);
- count = data[2] & 0xff;
- sequence = data[3] & 0xff;
-
- // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence
- // is zero, or sequence > count, ignore the entire element
- if (count == 0 || sequence == 0 || sequence > count) {
- referenceNumber = -1;
- }
- break;
- }
-
- case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: {
- byte[] data = element.getData();
-
- destPort = (data[0] & 0xff) << 8;
- destPort |= (data[1] & 0xff);
-
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- Log.e(TAG, "Bad element in header", e);
- return; // TODO: NACK the message or something, don't just discard.
- }
- }
- }
-
- if (referenceNumber == -1) {
- // notify everyone of the message if it isn't partial
+ /**
+ * TODO(cleanup): Since both CDMA and GSM use the same header
+ * format, this dispatch processing is naturally identical,
+ * and code should probably not be replicated explicitly.
+ */
+ // See if message is partial or port addressed.
+ if ((smsHeader == null) || (smsHeader.concatRef == null)) {
+ // Message is not partial (not part of concatenated sequence).
byte[][] pdus = new byte[1][];
pdus[0] = sms.getPdu();
- if (destPort != -1) {// GSM-style WAP indication
- if (destPort == SmsHeader.PORT_WAP_PUSH) {
+ if (smsHeader.portAddrs != null) {
+ if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
+ // GSM-style WAP indication
mWapPush.dispatchWapPdu(sms.getUserData());
}
- // The message was sent to a port, so concoct a URI for it
- dispatchPortAddressedPdus(pdus, destPort);
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
} else {
- // It's a normal message, dispatch it
+ // Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
} else {
- // Process the message part
- processMessagePart(sms, referenceNumber, sequence, count, destPort);
+ // Process the message part.
+ processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
}
}
@@ -314,41 +267,49 @@
}
/** {@inheritDoc} */
- protected void sendMultipartText(String destinationAddress, String scAddress,
+ protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) {
- int ref = ++sConcatenatedRef & 0xff;
+ /**
+ * TODO(cleanup): There is no real code difference between
+ * this and the GSM version, and hence it should be moved to
+ * the base class or consolidated somehow, provided calling
+ * the proper submitpdu stuff can be arranged.
+ */
- for (int i = 0, count = parts.size(); i < count; i++) {
- // build SmsHeader data
- byte[] data = new byte[5];
- data[0] = (byte) SmsHeader.CONCATENATED_8_BIT_REFERENCE;
- data[1] = (byte) 3; // 3 bytes follow
- data[2] = (byte) ref; // reference #, unique per message
- data[3] = (byte) count; // total part count
- data[4] = (byte) (i + 1); // 1-based sequence
+ int refNumber = getNextConcatenatedRef() & 0x00FF;
+
+ for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = refNumber;
+ concatRef.seqNumber = i + 1; // 1-based sequence
+ concatRef.msgCount = msgCount;
+ concatRef.isEightBits = true;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
PendingIntent sentIntent = null;
- PendingIntent deliveryIntent = null;
-
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
}
+
+ PendingIntent deliveryIntent = null;
if (deliveryIntents != null && deliveryIntents.size() > i) {
deliveryIntent = deliveryIntents.get(i);
}
- SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
- parts.get(i), deliveryIntent != null, data);
+ SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(scAddr, destAddr,
+ parts.get(i), deliveryIntent != null, smsHeader);
- sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
+ sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
}
}
- protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
+ protected void sendSubmitPdu(SmsMessage.SubmitPdu submitPdu, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
- super.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent);
+ sendRawPdu(submitPdu.encodedScAddress, submitPdu.encodedMessage,
+ sentIntent, deliveryIntent);
}
/** {@inheritDoc} */
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 343a22e..b2083ed 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -277,6 +277,15 @@
}
/**
+ * TODO(cleanup): why do getSubmitPdu methods take an scAddr input
+ * and do nothing with it? GSM allows us to specify a SC (eg,
+ * when responding to an SMS that explicitly requests the response
+ * is sent to a specific SC), or pass null to use the default
+ * value. Is there no similar notion in CDMA? Or do we just not
+ * have it hooked up?
+ */
+
+ /**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
* @param scAddr Service Centre address. Null means use default.
@@ -290,88 +299,53 @@
* Returns null on encode error.
* @hide
*/
- public static SubmitPdu getSubmitPdu(String scAddr,
- String destAddr, String message,
- boolean statusReportRequested, byte[] headerData) {
- /**
- * TODO(cleanup): why does this method take an scAddr input
- * and do nothing with it? GSM allows us to specify a SC (eg,
- * when responding to an SMS that explicitly requests the
- * response is sent to a specific SC), or pass null to use the
- * default value. Is there no similar notion in CDMA? Or do
- * we just not have it hooked up?
- */
+ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
+ boolean statusReportRequested, SmsHeader smsHeader) {
+ /**
+ * TODO(cleanup): Do we really want silent failure like this?
+ * Would it not be much more reasonable to make sure we don't
+ * call this function if we really want nothing done?
+ */
if (message == null || destAddr == null) {
return null;
}
UserData uData = new UserData();
uData.payloadStr = message;
- if(headerData != null) {
- /**
- * TODO(cleanup): we force the outside to deal with _all_
- * of the raw details of properly constructing serialized
- * headers, unserialze here, and then promptly reserialze
- * during encoding -- rather undesirable.
- */
- uData.userDataHeader = SmsHeader.parse(headerData);
- }
-
- return privateGetSubmitPdu(destAddr, statusReportRequested, uData, (headerData == null));
- }
-
-
- /**
- * Get an SMS-SUBMIT PDU for a destination address and a message
- *
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, String message,
- boolean statusReportRequested) {
- return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
+ uData.userDataHeader = smsHeader;
+ return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
}
/**
* Get an SMS-SUBMIT PDU for a data message to a destination address & port
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
- * @param destinationPort the port to deliver the message to at the
+ * @param scAddr Service Centre address. null == use default
+ * @param destAddr the address of the destination for the message
+ * @param destPort the port to deliver the message to at the
* destination
* @param data the data for the message
* @return a <code>SubmitPdu</code> containing the encoded SC
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destAddr, short destinationPort, byte[] data,
- boolean statusReportRequested) {
+ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, short destPort,
+ byte[] data, boolean statusReportRequested) {
/**
- * TODO(cleanup): if we had properly exposed SmsHeader
- * information, this mess of many getSubmitPdu public
- * interface methods that currently pollute the api could have
- * been much more cleanly collapsed into one.
+ * TODO(cleanup): this is not a general-purpose SMS creation
+ * method, but rather something specialized to messages
+ * containing OCTET encoded (meaning non-human-readable) user
+ * data. The name should reflect that, and not just overload.
*/
- /**
- * TODO(cleanup): header serialization should be put somewhere
- * canonical to allow proper debugging and reuse.
- */
- byte[] destPort = new byte[4];
- destPort[0] = (byte) ((destinationPort >> 8) & 0xFF); // MSB of destination port
- destPort[1] = (byte) (destinationPort & 0xFF); // LSB of destination port
- destPort[2] = 0x00; // MSB of originating port
- destPort[3] = 0x00; // LSB of originating port
+ SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
+ portAddrs.destPort = destPort;
+ portAddrs.origPort = 0;
+ portAddrs.areEightBits = false;
+
SmsHeader smsHeader = new SmsHeader();
- smsHeader.add(
- new SmsHeader.Element(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT, destPort));
- smsHeader.nbrOfHeaders = smsHeader.getElements().size();
+ smsHeader.portAddrs = portAddrs;
UserData uData = new UserData();
uData.userDataHeader = smsHeader;
@@ -379,7 +353,7 @@
uData.msgEncodingSet = true;
uData.payload = data;
- return privateGetSubmitPdu(destAddr, statusReportRequested, uData, true);
+ return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
}
static class PduParser {
@@ -445,31 +419,23 @@
* {@inheritDoc}
*/
public boolean isMWIClearMessage() {
- if ((mBearerData != null) && (0 == mBearerData.numberOfMessages)) {
- return true;
- }
- return false;
+ return ((mBearerData != null) && (mBearerData.numberOfMessages == 0));
}
/**
* {@inheritDoc}
*/
public boolean isMWISetMessage() {
- if ((mBearerData != null) && (mBearerData.numberOfMessages >0)) {
- return true;
- }
- return false;
+ return ((mBearerData != null) && (mBearerData.numberOfMessages > 0));
}
/**
* {@inheritDoc}
*/
public boolean isMwiDontStore() {
- if ((mBearerData != null) && (mBearerData.numberOfMessages >0)
- && (null == mBearerData.userData)) {
- return true;
- }
- return false;
+ return ((mBearerData != null) &&
+ (mBearerData.numberOfMessages > 0) &&
+ (mBearerData.userData == null));
}
/**
@@ -478,7 +444,7 @@
* shifted to the bits 31-16.
*/
public int getStatus() {
- return(status<<16);
+ return (status << 16);
}
/**
@@ -518,7 +484,7 @@
*/
private void parsePdu(byte[] pdu) {
ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
- DataInputStream dis = new DataInputStream(new BufferedInputStream(bais));
+ DataInputStream dis = new DataInputStream(bais);
byte length;
int bearerDataLength;
SmsEnvelope env = new SmsEnvelope();
@@ -568,38 +534,23 @@
protected void parseSms() {
mBearerData = BearerData.decode(mEnvelope.bearerData);
messageRef = mBearerData.messageId;
+ if (mBearerData.userData != null) {
+ userData = mBearerData.userData.payload;
+ userDataHeader = mBearerData.userData.userDataHeader;
+ messageBody = mBearerData.userData.payloadStr;
+ }
- // TP-Message-Type-Indicator
- // (See 3GPP2 C.S0015-B, v2, 4.5.1)
- int messageType = mBearerData.messageType;
-
- switch (messageType) {
+ // TP-Message-Type-Indicator (See 3GPP2 C.S0015-B, v2, 4.5.1)
+ switch (mBearerData.messageType) {
case BearerData.MESSAGE_TYPE_USER_ACK:
case BearerData.MESSAGE_TYPE_READ_ACK:
case BearerData.MESSAGE_TYPE_DELIVER:
- // Deliver (mobile-terminated only)
- parseSmsDeliver();
- break;
case BearerData.MESSAGE_TYPE_DELIVERY_ACK:
- parseSmsDeliveryAck();
break;
-
default:
- // the rest of these
- throw new RuntimeException("Unsupported message type: " + messageType);
+ throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
}
- }
- /**
- * TODO(cleanup): why are there two nearly identical functions
- * below? More rubbish...
- */
-
- /**
- * Parses a SMS-DELIVER message. (mobile-terminated only)
- * See 3GPP2 C.S0015-B, v2, 4.4.1
- */
- private void parseSmsDeliver() {
if (originatingAddress != null) {
originatingAddress.address = new String(originatingAddress.origBytes);
if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
@@ -612,46 +563,13 @@
if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
- parseUserData(mBearerData.userData);
- }
-
- /**
- * Parses a SMS-DELIVER message. (mobile-terminated only)
- * See 3GPP2 C.S0015-B, v2, 4.4.1
- */
- private void parseSmsDeliveryAck() {
- if (originatingAddress != null) {
- originatingAddress.address = new String(originatingAddress.origBytes);
- if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
- + originatingAddress.address);
- }
-
- if (mBearerData.timeStamp != null) {
- scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp);
- }
-
- if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
-
- if (mBearerData.errorClass != BearerData.ERROR_UNDEFINED) {
+ // TODO(Teleca): do we really want this test to occur only for DELIVERY_ACKs?
+ if ((mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK) &&
+ (mBearerData.errorClass != BearerData.ERROR_UNDEFINED)) {
status = mBearerData.errorClass << 8;
status |= mBearerData.messageStatus;
}
- parseUserData(mBearerData.userData);
- }
-
- /**
- * Copy parsed user data out from internal datastructures.
- */
- private void parseUserData(UserData uData) {
- if (uData == null) {
- return;
- }
-
- userData = uData.payload;
- userDataHeader = uData.userDataHeader;
- messageBody = uData.payloadStr;
-
if (messageBody != null) {
if (Config.LOGV) Log.v(LOG_TAG, "SMS message body: '" + messageBody + "'");
parseMessageBody();
@@ -708,7 +626,7 @@
* @return byte stream for SubmitPdu.
*/
private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
- UserData userData, boolean useNewId) {
+ UserData userData) {
/**
* TODO(cleanup): give this function a more meaningful name.
@@ -720,7 +638,7 @@
BearerData bearerData = new BearerData();
bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
- if (useNewId) setNextMessageId();
+ if (userData != null) setNextMessageId();
bearerData.messageId = nextMessageId;
bearerData.deliveryAckReq = statusReportRequested;
@@ -812,6 +730,15 @@
dos.write(env.bearerData, 0, env.bearerData.length);
dos.close();
+ /**
+ * TODO(cleanup) -- This is the only place where mPdu is
+ * defined, and this is not obviously the only place where
+ * it needs to be defined. It would be much nicer if
+ * accessing the serialized representation used a less
+ * fragile mechanism. Maybe the getPdu method could
+ * generate a representation if there was not yet one?
+ */
+
mPdu = baos.toByteArray();
} catch (IOException ex) {
Log.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex);
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index e64d022..05c8c9d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -189,8 +189,12 @@
public int messageStatus = STATUS_UNDEFINED;
/**
- * 1-bit value that indicates whether a User Data Header is present.
+ * 1-bit value that indicates whether a User Data Header (UDH) is present.
* (See 3GPP2 C.S0015-B, v2, 4.5.1)
+ *
+ * NOTE: during encoding, this value will be set based on the
+ * presence of a UDH in the structured data, any existing setting
+ * will be overwritten.
*/
public boolean hasUserDataHeader;
@@ -248,25 +252,27 @@
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append("BearerData:\n");
- builder.append(" messageType: " + messageType + "\n");
- builder.append(" messageId: " + (int)messageId + "\n");
- builder.append(" priority: " + (priorityIndicatorSet ? priority : "not set") + "\n");
- builder.append(" privacy: " + (privacyIndicatorSet ? privacy : "not set") + "\n");
- builder.append(" alert: " + (alertIndicatorSet ? alert : "not set") + "\n");
- builder.append(" displayMode: " + (displayModeSet ? displayMode : "not set") + "\n");
- builder.append(" language: " + (languageIndicatorSet ? language : "not set") + "\n");
- builder.append(" errorClass: " + (messageStatusSet ? errorClass : "not set") + "\n");
- builder.append(" msgStatus: " + (messageStatusSet ? messageStatus : "not set") + "\n");
- builder.append(" hasUserDataHeader: " + hasUserDataHeader + "\n");
- builder.append(" timeStamp: " + timeStamp + "\n");
- builder.append(" userAckReq: " + userAckReq + "\n");
- builder.append(" deliveryAckReq: " + deliveryAckReq + "\n");
- builder.append(" readAckReq: " + readAckReq + "\n");
- builder.append(" reportReq: " + reportReq + "\n");
- builder.append(" numberOfMessages: " + numberOfMessages + "\n");
- builder.append(" callbackNumber: " + callbackNumber + "\n");
- builder.append(" userData: " + userData + "\n");
+ builder.append("BearerData ");
+ builder.append("{ messageType=" + messageType);
+ builder.append(", messageId=" + (int)messageId);
+ builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
+ builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
+ builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
+ builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
+ builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
+ builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
+ builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
+ builder.append(", timeStamp=" +
+ ((timeStamp != null) ? HexDump.toHexString(timeStamp) : "unset"));
+ builder.append(", userAckReq=" + userAckReq);
+ builder.append(", deliveryAckReq=" + deliveryAckReq);
+ builder.append(", readAckReq=" + readAckReq);
+ builder.append(", reportReq=" + reportReq);
+ builder.append(", numberOfMessages=" + numberOfMessages);
+ builder.append(", callbackNumber=" + callbackNumber);
+ builder.append(", hasUserDataHeader=" + hasUserDataHeader);
+ builder.append(", userData=" + userData);
+ builder.append(" }");
return builder.toString();
}
@@ -335,12 +341,19 @@
private static void encodeUserDataPayload(UserData uData)
throws CodingException
{
+ byte[] headerData = null;
+ if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
+ int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet
+
+ byte[] payloadData;
if (uData.msgEncodingSet) {
if (uData.msgEncoding == UserData.ENCODING_OCTET) {
if (uData.payload == null) {
Log.e(LOG_TAG, "user data with octet encoding but null payload");
// TODO(code_review): reasonable for fail case? or maybe bail on encoding?
- uData.payload = new byte[0];
+ payloadData = new byte[0];
+ } else {
+ payloadData = uData.payload;
}
} else {
if (uData.payloadStr == null) {
@@ -349,11 +362,11 @@
uData.payloadStr = "";
}
if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
- uData.payload = encode7bitGsm(uData.payloadStr);
+ payloadData = encode7bitGsm(uData.payloadStr);
} else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
- uData.payload = encode7bitAscii(uData.payloadStr);
+ payloadData = encode7bitAscii(uData.payloadStr);
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
- uData.payload = encodeUtf16(uData.payloadStr);
+ payloadData = encodeUtf16(uData.payloadStr);
} else {
throw new CodingException("unsupported user data encoding (" +
uData.msgEncoding + ")");
@@ -367,19 +380,28 @@
uData.payloadStr = "";
}
try {
- uData.payload = encode7bitAscii(uData.payloadStr);
+ payloadData = encode7bitAscii(uData.payloadStr);
uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
} catch (CodingException ex) {
- uData.payload = encodeUtf16(uData.payloadStr);
+ payloadData = encodeUtf16(uData.payloadStr);
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
}
uData.msgEncodingSet = true;
uData.numFields = uData.payloadStr.length();
}
- if (uData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) {
- throw new CodingException("encoded user data too large (" + uData.payload.length +
+
+ int totalLength = payloadData.length + headerDataLen;
+ if (totalLength > SmsMessage.MAX_USER_DATA_BYTES) {
+ throw new CodingException("encoded user data too large (" + totalLength +
" > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
}
+
+ uData.payload = new byte[totalLength];
+ if (headerData != null) {
+ uData.payload[0] = (byte)headerData.length;
+ System.arraycopy(headerData, 0, uData.payload, 1, headerData.length);
+ }
+ System.arraycopy(payloadData, 0, uData.payload, headerDataLen, payloadData.length);
}
private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
@@ -394,11 +416,6 @@
*
*/
int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
- byte[] headerData = null;
- if (bData.hasUserDataHeader) {
- headerData = bData.userData.userDataHeader.toByteArray();
- dataBits += headerData.length * 8;
- }
int paramBits = dataBits + 13;
if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
(bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
@@ -413,7 +430,6 @@
outStream.write(8, bData.userData.msgType);
}
outStream.write(8, bData.userData.numFields);
- if (headerData != null) outStream.writeByteArray(headerData.length * 8, headerData);
outStream.writeByteArray(dataBits, bData.userData.payload);
if (paddingBits > 0) outStream.write(paddingBits, 0);
}
@@ -557,6 +573,8 @@
* @return data byta array of raw encoded SMS bearer data.
*/
public static byte[] encode(BearerData bData) {
+ bData.hasUserDataHeader = ((bData.userData != null) &&
+ (bData.userData.userDataHeader != null));
try {
BitwiseOutputStream outStream = new BitwiseOutputStream(200);
outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
@@ -723,11 +741,11 @@
{
int offset = 0;
if (hasUserDataHeader) {
- int udhLen = userData.payload[0];
- offset += udhLen;
+ int udhLen = userData.payload[0] & 0x00FF;
+ offset += udhLen + 1;
byte[] headerData = new byte[udhLen];
System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
- userData.userDataHeader = SmsHeader.parse(headerData);
+ userData.userDataHeader = SmsHeader.fromByteArray(headerData);
}
switch (userData.msgEncoding) {
case UserData.ENCODING_OCTET:
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 02e94ad..7c37bc2 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -93,14 +93,15 @@
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append("UserData:\n");
- builder.append(" msgEncoding: " + (msgEncodingSet ? msgEncoding : "not set") + "\n");
- builder.append(" msgType: " + msgType + "\n");
- builder.append(" paddingBits: " + paddingBits + "\n");
- builder.append(" numFields: " + (int)numFields + "\n");
- builder.append(" userDataHeader: " + userDataHeader + "\n");
- builder.append(" payload: '" + HexDump.toHexString(payload) + "'");
- builder.append(", payloadStr: '" + payloadStr + "'");
+ builder.append("UserData ");
+ builder.append("{ msgEncoding=" + (msgEncodingSet ? msgEncoding : "unset"));
+ builder.append(", msgType=" + msgType);
+ builder.append(", paddingBits=" + paddingBits);
+ builder.append(", numFields=" + (int)numFields);
+ builder.append(", userDataHeader=" + userDataHeader);
+ builder.append(", payload='" + HexDump.toHexString(payload) + "'");
+ builder.append(", payloadStr='" + payloadStr + "'");
+ builder.append(" }");
return builder.toString();
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 4fe1ea0..3459dcd 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -68,6 +68,7 @@
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.PhoneSubInfo;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.gsm.stk.StkService;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.IccVmNotSupportedException;
@@ -204,9 +205,9 @@
}
}
- //Change the system setting
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.GSM_PHONE);
+ //Change the system property
+ SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
+ new Integer(RILConstants.GSM_PHONE).toString());
}
public void dispose() {
@@ -837,21 +838,21 @@
private void storeVoiceMailNumber(String number) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
- editor.putString(VM_NUMBER, number);
+ editor.putString(VM_NUMBER, number);
editor.commit();
setVmSimImsi(getSubscriberId());
}
public String getVoiceMailNumber() {
// Read from the SIM. If its null, try reading from the shared preference area.
- String number = mSIMRecords.getVoiceMailNumber();
+ String number = mSIMRecords.getVoiceMailNumber();
if (TextUtils.isEmpty(number)) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
number = sp.getString(VM_NUMBER, null);
- }
+ }
return number;
}
-
+
private String getVmSimImsi() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
return sp.getString(VM_SIM_IMSI, null);
@@ -863,7 +864,7 @@
editor.putString(VM_SIM_IMSI, imsi);
editor.commit();
}
-
+
public String getVoiceMailAlphaTag() {
String ret;
@@ -932,13 +933,13 @@
public void setVoiceMailNumber(String alphaTag,
String voiceMailNumber,
Message onComplete) {
-
- Message resp;
+
+ Message resp;
mVmNumber = voiceMailNumber;
resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
mSIMRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
-
+
private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
switch (commandInterfaceCFReason) {
case CF_REASON_UNCONDITIONAL:
@@ -1317,11 +1318,11 @@
case EVENT_SIM_RECORDS_LOADED:
updateCurrentCarrierInProvider();
-
+
// Check if this is a different SIM than the previous one. If so unset the
// voice mail number.
String imsi = getVmSimImsi();
- if (imsi != null && !getSubscriberId().equals(imsi)) {
+ if (imsi != null && !getSubscriberId().equals(imsi)) {
storeVoiceMailNumber(null);
setVmSimImsi(null);
}
@@ -1403,7 +1404,7 @@
onComplete.sendToTarget();
}
break;
-
+
case EVENT_SET_VM_NUMBER_DONE:
ar = (AsyncResult)msg.obj;
if (IccVmNotSupportedException.class.isInstance(ar.exception)) {
@@ -1417,7 +1418,7 @@
}
break;
-
+
case EVENT_GET_CALL_FORWARD_DONE:
ar = (AsyncResult)msg.obj;
if (ar.exception == null) {
@@ -1460,7 +1461,7 @@
/**
* Sets the "current" field in the telephony provider according to the SIM's operator
- *
+ *
* @return true for success; false otherwise.
*/
boolean updateCurrentCarrierInProvider() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 3e73caf..2fce188 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -39,8 +39,11 @@
final class GsmSMSDispatcher extends SMSDispatcher {
private static final String TAG = "GSM";
+ private GSMPhone mGsmPhone;
+
GsmSMSDispatcher(GSMPhone phone) {
super(phone);
+ mGsmPhone = phone;
}
/**
@@ -97,110 +100,41 @@
// Special case the message waiting indicator messages
if (sms.isMWISetMessage()) {
- ((GSMPhone) mPhone).updateMessageWaitingIndicator(true);
-
- if (sms.isMwiDontStore()) {
- handled = true;
- }
-
+ mGsmPhone.updateMessageWaitingIndicator(true);
+ handled |= sms.isMwiDontStore();
if (Config.LOGD) {
- Log.d(TAG,
- "Received voice mail indicator set SMS shouldStore="
- + !handled);
+ Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
}
} else if (sms.isMWIClearMessage()) {
- ((GSMPhone) mPhone).updateMessageWaitingIndicator(false);
-
- if (sms.isMwiDontStore()) {
- handled = true;
- }
-
+ mGsmPhone.updateMessageWaitingIndicator(false);
+ handled |= sms.isMwiDontStore();
if (Config.LOGD) {
- Log.d(TAG,
- "Received voice mail indicator clear SMS shouldStore="
- + !handled);
+ Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
}
}
- if (handled) {
- return;
- }
+ if (handled) return;
- // Parse the headers to see if this is partial, or port addressed
- int referenceNumber = -1;
- int count = 0;
- int sequence = 0;
- int destPort = -1;
-
- SmsHeader header = sms.getUserDataHeader();
- if (header != null) {
- for (SmsHeader.Element element : header.getElements()) {
- try {
- switch (element.getID()) {
- case SmsHeader.CONCATENATED_8_BIT_REFERENCE: {
- byte[] data = element.getData();
-
- referenceNumber = data[0] & 0xff;
- count = data[1] & 0xff;
- sequence = data[2] & 0xff;
-
- // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence
- // is zero, or sequence > count, ignore the entire element
- if (count == 0 || sequence == 0 || sequence > count) {
- referenceNumber = -1;
- }
- break;
- }
-
- case SmsHeader.CONCATENATED_16_BIT_REFERENCE: {
- byte[] data = element.getData();
-
- referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff);
- count = data[2] & 0xff;
- sequence = data[3] & 0xff;
-
- // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence
- // is zero, or sequence > count, ignore the entire element
- if (count == 0 || sequence == 0 || sequence > count) {
- referenceNumber = -1;
- }
- break;
- }
-
- case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: {
- byte[] data = element.getData();
-
- destPort = (data[0] & 0xff) << 8;
- destPort |= (data[1] & 0xff);
-
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- Log.e(TAG, "Bad element in header", e);
- return; // TODO: NACK the message or something, don't just discard.
- }
- }
- }
-
- if (referenceNumber == -1) {
- // notify everyone of the message if it isn't partial
+ SmsHeader smsHeader = sms.getUserDataHeader();
+ // See if message is partial or port addressed.
+ if ((smsHeader == null) || (smsHeader.concatRef == null)) {
+ // Message is not partial (not part of concatenated sequence).
byte[][] pdus = new byte[1][];
pdus[0] = sms.getPdu();
- if (destPort != -1) {
- if (destPort == SmsHeader.PORT_WAP_PUSH) {
+ if (smsHeader.portAddrs != null) {
+ if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
mWapPush.dispatchWapPdu(sms.getUserData());
}
- // The message was sent to a port, so concoct a URI for it
- dispatchPortAddressedPdus(pdus, destPort);
+ // The message was sent to a port, so concoct a URI for it.
+ dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
} else {
- // It's a normal message, dispatch it
+ // Normal short and non-port-addressed message, dispatch it.
dispatchPdus(pdus);
}
} else {
- // Process the message part
- processMessagePart(sms, referenceNumber, sequence, count, destPort);
+ // Process the message part.
+ processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
}
}
@@ -208,28 +142,30 @@
protected void sendMultipartText(String destinationAddress, String scAddress,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) {
- int ref = ++sConcatenatedRef & 0xff;
- for (int i = 0, count = parts.size(); i < count; i++) {
- // build SmsHeader
- byte[] data = new byte[3];
- data[0] = (byte) ref; // reference #, unique per message
- data[1] = (byte) count; // total part count
- data[2] = (byte) (i + 1); // 1-based sequence
- SmsHeader header = new SmsHeader();
- header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
+ int refNumber = getNextConcatenatedRef() & 0x00FF;
+
+ for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = refNumber;
+ concatRef.seqNumber = i + 1; // 1-based sequence
+ concatRef.msgCount = msgCount;
+ concatRef.isEightBits = false;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
+
PendingIntent sentIntent = null;
- PendingIntent deliveryIntent = null;
-
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
}
+
+ PendingIntent deliveryIntent = null;
if (deliveryIntents != null && deliveryIntents.size() > i) {
deliveryIntent = deliveryIntents.get(i);
}
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
- parts.get(i), deliveryIntent != null, header.toByteArray());
+ parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader));
sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
}
@@ -259,18 +195,16 @@
* to the recipient. The raw pdu of the status report is in the
* extended data ("pdu").
*/
- private void sendMultipartTextWithPermit(String destinationAddress,
+ private void sendMultipartTextWithPermit(String destinationAddress,
String scAddress, ArrayList<String> parts,
- ArrayList<PendingIntent> sentIntents,
+ ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) {
-
- PendingIntent sentIntent = null;
- PendingIntent deliveryIntent = null;
-
+
// check if in service
int ss = mPhone.getServiceState().getState();
if (ss != ServiceState.STATE_IN_SERVICE) {
for (int i = 0, count = parts.size(); i < count; i++) {
+ PendingIntent sentIntent = null;
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
}
@@ -280,26 +214,29 @@
return;
}
- int ref = ++sConcatenatedRef & 0xff;
+ int refNumber = getNextConcatenatedRef() & 0x00FF;
- for (int i = 0, count = parts.size(); i < count; i++) {
- // build SmsHeader
- byte[] data = new byte[3];
- data[0] = (byte) ref; // reference #, unique per message
- data[1] = (byte) count; // total part count
- data[2] = (byte) (i + 1); // 1-based sequence
- SmsHeader header = new SmsHeader();
- header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
-
+ for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = refNumber;
+ concatRef.seqNumber = i + 1; // 1-based sequence
+ concatRef.msgCount = msgCount;
+ concatRef.isEightBits = false;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
+
+ PendingIntent sentIntent = null;
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
}
+
+ PendingIntent deliveryIntent = null;
if (deliveryIntents != null && deliveryIntents.size() > i) {
deliveryIntent = deliveryIntents.get(i);
}
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
- parts.get(i), deliveryIntent != null, header.toByteArray());
+ parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader));
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("smsc", pdus.encodedScAddress);
@@ -307,7 +244,7 @@
SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent);
sendSms(tracker);
- }
+ }
}
/** {@inheritDoc} */
@@ -376,4 +313,3 @@
}
}
-
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 867b719..ed61c3f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -330,9 +330,20 @@
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, short destinationPort, byte[] data,
boolean statusReportRequested) {
- if (data.length > (MAX_USER_DATA_BYTES - 7 /* UDH size */)) {
+
+ SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
+ portAddrs.destPort = destinationPort;
+ portAddrs.origPort = 0;
+ portAddrs.areEightBits = false;
+
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.portAddrs = portAddrs;
+
+ byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
+
+ if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
Log.e(LOG_TAG, "SMS data message may only contain "
- + (MAX_USER_DATA_BYTES - 7) + " bytes");
+ + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
return null;
}
@@ -348,21 +359,12 @@
// (no TP-Validity-Period)
- // User data size
- bo.write(data.length + 7);
+ // Total size
+ bo.write(data.length + smsHeaderData.length + 1);
- // User data header size
- bo.write(0x06); // header is 6 octets
-
- // User data header, indicating the destination port
- bo.write(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT); // port
- // addressing
- // header
- bo.write(0x04); // each port is 2 octets
- bo.write((destinationPort >> 8) & 0xFF); // MSB of destination port
- bo.write(destinationPort & 0xFF); // LSB of destination port
- bo.write(0x00); // MSB of originating port
- bo.write(0x00); // LSB of originating port
+ // User data header
+ bo.write(smsHeaderData.length);
+ bo.write(smsHeaderData, 0, smsHeaderData.length);
// User data
bo.write(data, 0, data.length);
@@ -562,7 +564,7 @@
byte[] udh = new byte[userDataHeaderLength];
System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength);
- userDataHeader = SmsHeader.parse(udh);
+ userDataHeader = SmsHeader.fromByteArray(udh);
offset += userDataHeaderLength;
int headerBits = (userDataHeaderLength + 1) * 8;
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index b3e88e1..f8d5d4d 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -136,6 +136,81 @@
}
@SmallTest
+ public void testUserDataHeaderConcatRefFeedback() throws Exception {
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+ bearerData.messageId = 55;
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = 0xEE;
+ concatRef.msgCount = 2;
+ concatRef.seqNumber = 2;
+ concatRef.isEightBits = true;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
+ byte[] encodedHeader = SmsHeader.toByteArray(smsHeader);
+ SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader);
+ assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
+ assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
+ assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
+ assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
+ assertEquals(decodedHeader.portAddrs, null);
+ UserData userData = new UserData();
+ userData.payloadStr = "User Data Header (UDH) feedback test";
+ userData.userDataHeader = smsHeader;
+ bearerData.userData = userData;
+ byte[] encodedSms = BearerData.encode(bearerData);
+ BearerData revBearerData = BearerData.decode(encodedSms);
+ decodedHeader = revBearerData.userData.userDataHeader;
+ assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
+ assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
+ assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
+ assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
+ assertEquals(decodedHeader.portAddrs, null);
+ }
+
+ @SmallTest
+ public void testUserDataHeaderMixedFeedback() throws Exception {
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+ bearerData.messageId = 42;
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = 0x34;
+ concatRef.msgCount = 5;
+ concatRef.seqNumber = 2;
+ concatRef.isEightBits = false;
+ SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
+ portAddrs.destPort = 88;
+ portAddrs.origPort = 66;
+ portAddrs.areEightBits = false;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
+ smsHeader.portAddrs = portAddrs;
+ byte[] encodedHeader = SmsHeader.toByteArray(smsHeader);
+ SmsHeader decodedHeader = SmsHeader.fromByteArray(encodedHeader);
+ assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
+ assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
+ assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
+ assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
+ assertEquals(decodedHeader.portAddrs.destPort, portAddrs.destPort);
+ assertEquals(decodedHeader.portAddrs.origPort, portAddrs.origPort);
+ assertEquals(decodedHeader.portAddrs.areEightBits, portAddrs.areEightBits);
+ UserData userData = new UserData();
+ userData.payloadStr = "User Data Header (UDH) feedback test";
+ userData.userDataHeader = smsHeader;
+ bearerData.userData = userData;
+ byte[] encodedSms = BearerData.encode(bearerData);
+ BearerData revBearerData = BearerData.decode(encodedSms);
+ decodedHeader = revBearerData.userData.userDataHeader;
+ assertEquals(decodedHeader.concatRef.refNumber, concatRef.refNumber);
+ assertEquals(decodedHeader.concatRef.msgCount, concatRef.msgCount);
+ assertEquals(decodedHeader.concatRef.seqNumber, concatRef.seqNumber);
+ assertEquals(decodedHeader.concatRef.isEightBits, concatRef.isEightBits);
+ assertEquals(decodedHeader.portAddrs.destPort, portAddrs.destPort);
+ assertEquals(decodedHeader.portAddrs.origPort, portAddrs.origPort);
+ assertEquals(decodedHeader.portAddrs.areEightBits, portAddrs.areEightBits);
+ }
+
+ @SmallTest
public void testReplyOption() throws Exception {
String pdu1 = "0003104090011648b6a794e0705476bf77bceae934fe5f6d94d87450080a0180";
BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
index 360352b..9d44fd9 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
@@ -69,10 +69,15 @@
SmsHeader header = sms.getUserDataHeader();
assertNotNull(header);
-
- Iterator<SmsHeader.Element> elements = header.getElements().iterator();
- assertNotNull(elements);
-
+ assertNotNull(header.concatRef);
+ assertEquals(header.concatRef.refNumber, 42);
+ assertEquals(header.concatRef.msgCount, 2);
+ assertEquals(header.concatRef.seqNumber, 1);
+ assertEquals(header.concatRef.isEightBits, true);
+ assertNotNull(header.portAddrs);
+ assertEquals(header.portAddrs.destPort, 2948);
+ assertEquals(header.portAddrs.origPort, 9200);
+ assertEquals(header.portAddrs.areEightBits, false);
pdu = "07914140279510F6440A8111110301003BF56080207130238A3B0B05040B8423F"
+ "000032A0202362E3130322E3137312E3135302F524E453955304A6D7135514141"
@@ -81,9 +86,15 @@
header = sms.getUserDataHeader();
assertNotNull(header);
-
- elements = header.getElements().iterator();
- assertNotNull(elements);
+ assertNotNull(header.concatRef);
+ assertEquals(header.concatRef.refNumber, 42);
+ assertEquals(header.concatRef.msgCount, 2);
+ assertEquals(header.concatRef.seqNumber, 2);
+ assertEquals(header.concatRef.isEightBits, true);
+ assertNotNull(header.portAddrs);
+ assertEquals(header.portAddrs.destPort, 2948);
+ assertEquals(header.portAddrs.origPort, 9200);
+ assertEquals(header.portAddrs.areEightBits, false);
/*
* UCS-2 encoded SMS
diff --git a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java
index 5df8991..e2336f8 100644
--- a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java
@@ -28,18 +28,20 @@
@SmallTest
public void test7bitWithHeader() throws Exception {
- byte[] data = new byte[3];
- data[0] = (byte) 1;
- data[1] = (byte) 2;
- data[2] = (byte) 2;
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = 1;
+ concatRef.seqNumber = 2;
+ concatRef.msgCount = 2;
+ concatRef.isEightBits = true;
SmsHeader header = new SmsHeader();
- header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
+ header.concatRef = concatRef;
- String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
- byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header.toByteArray());
+ String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
+ byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message,
+ SmsHeader.toByteArray(header));
int septetCount = GsmAlphabet.countGsmSeptets(message, false);
String parsedMessage = GsmAlphabet.gsm7BitPackedToString(
- userData, header.toByteArray().length+1, septetCount, 1);
+ userData, SmsHeader.toByteArray(header).length+1, septetCount, 1);
assertEquals(message, parsedMessage);
}
@@ -306,4 +308,3 @@
GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
}
}
-
diff --git a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java b/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java
index 5d5d1f9..8a66614 100644
--- a/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/SMSDispatcherTest.java
@@ -34,35 +34,38 @@
public void testCMT1() throws Exception {
SmsMessage sms;
SmsHeader header;
- Iterator<SmsHeader.Element> elements;
String[] lines = new String[2];
-
- lines[0] = "+CMT: ,158";
+
+ lines[0] = "+CMT: ,158";
lines[1] = "07914140279510F6440A8111110301003BF56080426101748A8C0B05040B"
+ "8423F000035502010106276170706C69636174696F6E2F766E642E776170"
+ "2E6D6D732D6D65737361676500AF848D0185B4848C8298524F347839776F"
+ "7547514D4141424C3641414141536741415A4B554141414141008D908918"
+ "802B31363530323438363137392F545950453D504C4D4E008A808E028000"
+ "88058103093A8083687474703A2F2F36";
-
+
sms = SmsMessage.newFromCMT(lines);
header = sms.getUserDataHeader();
assertNotNull(header);
assertNotNull(sms.getUserData());
-
- elements = header.getElements().iterator();
- assertNotNull(elements);
+ assertNotNull(header.concatRef);
+ assertEquals(header.concatRef.refNumber, 85);
+ assertEquals(header.concatRef.msgCount, 2);
+ assertEquals(header.concatRef.seqNumber, 1);
+ assertEquals(header.concatRef.isEightBits, true);
+ assertNotNull(header.portAddrs);
+ assertEquals(header.portAddrs.destPort, 2948);
+ assertEquals(header.portAddrs.origPort, 9200);
+ assertEquals(header.portAddrs.areEightBits, false);
}
-
+
@MediumTest
public void testCMT2() throws Exception {
SmsMessage sms;
SmsHeader header;
- Iterator<SmsHeader.Element> elements;
String[] lines = new String[2];
-
lines[0] = "+CMT: ,77";
lines[1] = "07914140279510F6440A8111110301003BF56080426101848A3B0B05040B8423F"
@@ -71,12 +74,17 @@
sms = SmsMessage.newFromCMT(lines);
header = sms.getUserDataHeader();
- System.out.println("header = " + header);
assertNotNull(header);
assertNotNull(sms.getUserData());
-
- elements = header.getElements().iterator();
- assertNotNull(elements);
+ assertNotNull(header.concatRef);
+ assertEquals(header.concatRef.refNumber, 85);
+ assertEquals(header.concatRef.msgCount, 2);
+ assertEquals(header.concatRef.seqNumber, 2);
+ assertEquals(header.concatRef.isEightBits, true);
+ assertNotNull(header.portAddrs);
+ assertEquals(header.portAddrs.destPort, 2948);
+ assertEquals(header.portAddrs.origPort, 9200);
+ assertEquals(header.portAddrs.areEightBits, false);
}
@MediumTest