Merge "GPS: Remove unnecessary and broken test in AgpsRilInterface updateNetworkState glue" into gingerbread
diff --git a/Android.mk b/Android.mk
index 6dc39eb..ab30c6e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -116,6 +116,14 @@
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/IThrottleManager.aidl \
+ core/java/android/nfc/ILlcpConnectionlessSocket.aidl \
+ core/java/android/nfc/ILlcpServiceSocket.aidl \
+ core/java/android/nfc/ILlcpSocket.aidl \
+ core/java/android/nfc/INdefTag.aidl \
+ core/java/android/nfc/INfcAdapter.aidl \
+ core/java/android/nfc/INfcTag.aidl \
+ core/java/android/nfc/IP2pInitiator.aidl \
+ core/java/android/nfc/IP2pTarget.aidl \
core/java/android/os/IHardwareService.aidl \
core/java/android/os/IMessenger.aidl \
core/java/android/os/INetworkManagementService.aidl \
@@ -159,14 +167,6 @@
core/java/com/android/internal/view/IInputMethodClient.aidl \
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
- core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl \
- core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl \
- core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl \
- core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl \
- core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl \
- core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl \
- core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl \
- core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl \
location/java/android/location/IGeocodeProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
@@ -249,6 +249,10 @@
frameworks/base/core/java/android/content/res/Configuration.aidl \
frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
frameworks/base/core/java/android/net/Uri.aidl \
+ frameworks/base/core/java/android/nfc/NdefMessage.aidl \
+ frameworks/base/core/java/android/nfc/NdefRecord.aidl \
+ frameworks/base/core/java/android/nfc/Tag.aidl \
+ frameworks/base/core/java/android/nfc/NdefTag.aidl \
frameworks/base/core/java/android/os/Bundle.aidl \
frameworks/base/core/java/android/os/DropBoxManager.aidl \
frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5618eaa..da1d46f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -68,6 +68,8 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverbtest_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/trustedlogic)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/com/trustedlogic)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.xml b/api/current.xml
index 2a2200d..3c2662d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -102984,6 +102984,33 @@
</package>
<package name="android.nfc"
>
+<class name="FormatException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="FormatException"
+ type="android.nfc.FormatException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="FormatException"
+ type="android.nfc.FormatException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
<class name="NdefMessage"
extends="java.lang.Object"
abstract="false"
@@ -103323,6 +103350,632 @@
>
</field>
</class>
+<class name="NdefTag"
+ extends="android.nfc.Tag"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="getNdefMessages"
+ return="android.nfc.NdefMessage[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNdefMessages"
+ return="android.nfc.NdefMessage[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.String">
+</parameter>
+</method>
+<method name="getNdefTargets"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_MIFARE_CLASSIC"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""type_mifare_classic""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_OTHER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""other""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_TYPE_1"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""type_1""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_TYPE_2"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""type_2""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_TYPE_3"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""type_3""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_TYPE_4"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""type_4""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="NdefTagConnection"
+ extends="android.nfc.RawTagConnection"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getModeHint"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="makeReadOnly"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="readNdefMessages"
+ return="android.nfc.NdefMessage[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="writeNdefMessage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="android.nfc.NdefMessage">
+</parameter>
+<exception name="FormatException" type="android.nfc.FormatException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<field name="NDEF_MODE_READ_ONCE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NDEF_MODE_READ_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NDEF_MODE_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NDEF_MODE_WRITE_MANY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NDEF_MODE_WRITE_ONCE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="NfcAdapter"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="createNdefTagConnection"
+ return="android.nfc.NdefTagConnection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="android.nfc.NdefTag">
+</parameter>
+</method>
+<method name="createNdefTagConnection"
+ return="android.nfc.NdefTagConnection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="android.nfc.NdefTag">
+</parameter>
+<parameter name="target" type="java.lang.String">
+</parameter>
+</method>
+<method name="createRawTagConnection"
+ return="android.nfc.RawTagConnection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+</method>
+<method name="createRawTagConnection"
+ return="android.nfc.RawTagConnection"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="android.nfc.Tag">
+</parameter>
+<parameter name="target" type="java.lang.String">
+</parameter>
+</method>
+<method name="getDefaultAdapter"
+ return="android.nfc.NfcAdapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocalNdefMessage"
+ return="android.nfc.NdefMessage"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isTagDiscoveryEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setLocalNdefMessage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="android.nfc.NdefMessage">
+</parameter>
+</method>
+<field name="ACTION_NDEF_TAG_DISCOVERED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.nfc.action.NDEF_TAG_DISCOVERED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_TAG_DISCOVERED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.nfc.action.TAG_DISCOVERED""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_TAG"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.nfc.extra.TAG""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="RawTagConnection"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="connect"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getTag"
+ return="android.nfc.Tag"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTagTarget"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="transceive"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="Tag"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRawTargets"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_ISO_14443_3A"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""iso14443_3a""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_ISO_14443_3B"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""iso14443_3b""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_ISO_14443_3B_PRIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""iso14443_3b""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_ISO_14443_4"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""iso14443_4""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_ISO_15693"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""iso15693""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_JIS_X_6319_4"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""jis_x_6319_4""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_OTHER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""other""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TARGET_TOPAZ"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""topaz""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
</package>
<package name="android.opengl"
>
@@ -131704,9 +132357,97 @@
>
<parameter name="path" type="java.lang.String">
</parameter>
-<parameter name="state" type="java.lang.String">
+<parameter name="state" type="int">
</parameter>
</method>
+<field name="ERROR_ALREADY_MOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="24"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_COULD_NOT_MOUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="21"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_COULD_NOT_UNMOUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="22"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_INTERNAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="20"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_NOT_MOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="23"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_PERMISSION_DENIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="25"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNMOUNTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="StorageManager"
extends="java.lang.Object"
@@ -131741,8 +132482,6 @@
>
<parameter name="filename" type="java.lang.String">
</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
</method>
<method name="mountObb"
return="boolean"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c08f1fc..fda08f6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -102,9 +102,6 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
-import com.trustedlogic.trustednfc.android.NfcManager;
-import com.trustedlogic.trustednfc.android.INfcManager;
-
import com.android.internal.os.IDropBoxManagerService;
import java.io.File;
@@ -173,7 +170,6 @@
private static ThrottleManager sThrottleManager;
private static WifiManager sWifiManager;
private static LocationManager sLocationManager;
- private static NfcManager sNfcManager;
private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
new HashMap<String, SharedPreferencesImpl>();
@@ -972,8 +968,6 @@
return getClipboardManager();
} else if (WALLPAPER_SERVICE.equals(name)) {
return getWallpaperManager();
- } else if (NFC_SERVICE.equals(name)) {
- return getNfcManager();
} else if (DROPBOX_SERVICE.equals(name)) {
return getDropBoxManager();
} else if (DEVICE_POLICY_SERVICE.equals(name)) {
@@ -1209,21 +1203,6 @@
return mDownloadManager;
}
- private NfcManager getNfcManager()
- {
- synchronized (sSync) {
- if (sNfcManager == null) {
- IBinder b = ServiceManager.getService(NFC_SERVICE);
- if (b == null) {
- return null;
- }
- INfcManager service = INfcManager.Stub.asInterface(b);
- sNfcManager = new NfcManager(service, mMainThread.getHandler());
- }
- }
- return sNfcManager;
- }
-
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index df1d960..ff82d39 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -516,7 +516,7 @@
}
break;
case DISCONNECT_A2DP_OUTGOING:
- processCommand(DISCONNECT_A2DP_OUTGOING);
+ deferMessage(message);
break;
case DISCONNECT_A2DP_INCOMING:
// Ignore, will be handled by Bluez
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7154aee..7242803 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5267,7 +5267,14 @@
b.append(' ');
}
first = false;
- b.append("dat=").append(mData);
+ b.append("dat=");
+ if (mData.getScheme().equalsIgnoreCase("tel")) {
+ b.append("tel:xxx-xxx-xxxx");
+ } else if (mData.getScheme().equalsIgnoreCase("smsto")) {
+ b.append("smsto:xxx-xxx-xxxx");
+ } else {
+ b.append(mData);
+ }
}
if (mType != null) {
if (!first) {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index f9a1637..e1a9dbb 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -411,7 +411,7 @@
}
public void setSyncAutomatically(Account account, String providerName, boolean sync) {
- Log.d(TAG, "setSyncAutomatically: " + account + ", provider " + providerName
+ Log.d(TAG, "setSyncAutomatically: " + /*account +*/ ", provider " + providerName
+ " -> " + sync);
synchronized (mAuthorities) {
AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
similarity index 83%
rename from core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java
rename to core/java/android/nfc/ErrorCodes.java
index ca3b7e0..5b76d84 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2010, 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
+ * 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,
@@ -14,19 +14,13 @@
* limitations under the License.
*/
-/**
- * File : ErrorCodes.java
- * Original-Author : Trusted Logic S.A. (Sylvain Fonteneau)
- * Created : 26-02-2010
- */
-
-package com.trustedlogic.trustednfc.android.internal;
+package android.nfc;
/**
* This class defines all the error codes that can be returned by the service
* and producing an exception on the application level. These are needed since
* binders does not support exceptions.
- *
+ *
* @hide
*/
public class ErrorCodes {
@@ -40,7 +34,7 @@
}
public static final int SUCCESS = 0;
-
+
public static final int ERROR_IO = -1;
public static final int ERROR_CANCELLED = -2;
@@ -58,34 +52,29 @@
public static final int ERROR_WRITE = -7;
public static final int ERROR_INVALID_PARAM = -8;
-
+
public static final int ERROR_INSUFFICIENT_RESOURCES = -9;
-
+
public static final int ERROR_SOCKET_CREATION = -10;
-
+
public static final int ERROR_SOCKET_NOT_CONNECTED = -11;
-
+
public static final int ERROR_BUFFER_TO_SMALL = -12;
public static final int ERROR_SAP_USED = -13;
-
+
public static final int ERROR_SERVICE_NAME_USED = -14;
-
+
public static final int ERROR_SOCKET_OPTIONS = -15;
-
+
public static final int ERROR_NFC_ON = -16;
-
+
public static final int ERROR_NOT_INITIALIZED = -17;
-
+
public static final int ERROR_SE_ALREADY_SELECTED = -18;
-
+
public static final int ERROR_SE_CONNECTED = -19;
-
+
public static final int ERROR_NO_SE_CONNECTED = -20;
-
-
-
-
-
-
-}
+
+}
\ No newline at end of file
diff --git a/core/java/android/nfc/FormatException.java b/core/java/android/nfc/FormatException.java
new file mode 100644
index 0000000..21a7c3b
--- /dev/null
+++ b/core/java/android/nfc/FormatException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010, 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.nfc;
+
+//TODO(npelly) javadoc or consider alternatives
+public class FormatException extends Exception {
+ public FormatException() {
+ super();
+ }
+
+ public FormatException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl b/core/java/android/nfc/ILlcpConnectionlessSocket.aidl
similarity index 69%
copy from core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
copy to core/java/android/nfc/ILlcpConnectionlessSocket.aidl
index 96819ae..c6d84e5 100644
--- a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
+++ b/core/java/android/nfc/ILlcpConnectionlessSocket.aidl
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
+
+import android.nfc.LlcpPacket;
/**
- * TODO
- *
- * {@hide}
+ * @hide
*/
-interface IP2pInitiator
+interface ILlcpConnectionlessSocket
{
-
- byte[] getGeneralBytes(int nativeHandle);
- int getMode(int nativeHandle);
- byte[] receive(int nativeHandle);
- boolean send(int nativeHandle, in byte[] data);
-
+ void close(int nativeHandle);
+ int getSap(int nativeHandle);
+ LlcpPacket receiveFrom(int nativeHandle);
+ int sendTo(int nativeHandle, in LlcpPacket packet);
}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl b/core/java/android/nfc/ILlcpServiceSocket.aidl
similarity index 73%
rename from core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl
rename to core/java/android/nfc/ILlcpServiceSocket.aidl
index 5eb1f3c..c3108dc 100644
--- a/core/java/com/trustedlogic/trustednfc/android/ILlcpServiceSocket.aidl
+++ b/core/java/android/nfc/ILlcpServiceSocket.aidl
@@ -14,19 +14,15 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
/**
- * TODO
- *
* {@hide}
*/
interface ILlcpServiceSocket
{
-
- int accept(int nativeHandle);
- void close(int nativeHandle);
- int getAcceptTimeout(int nativeHandle);
- void setAcceptTimeout(int nativeHandle, int timeout);
-
-}
\ No newline at end of file
+ int accept(int nativeHandle);
+ void close(int nativeHandle);
+ int getAcceptTimeout(int nativeHandle);
+ void setAcceptTimeout(int nativeHandle, int timeout);
+}
diff --git a/core/java/android/nfc/ILlcpSocket.aidl b/core/java/android/nfc/ILlcpSocket.aidl
new file mode 100644
index 0000000..dda5628
--- /dev/null
+++ b/core/java/android/nfc/ILlcpSocket.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+/**
+ * @hide
+ */
+interface ILlcpSocket
+{
+ int close(int nativeHandle);
+ int connect(int nativeHandle, int sap);
+ int connectByName(int nativeHandle, String sn);
+ int getConnectTimeout(int nativeHandle);
+ int getLocalSap(int nativeHandle);
+ int getLocalSocketMiu(int nativeHandle);
+ int getLocalSocketRw(int nativeHandle);
+ int getRemoteSocketMiu(int nativeHandle);
+ int getRemoteSocketRw(int nativeHandle);
+ int receive(int nativeHandle, out byte[] receiveBuffer);
+ int send(int nativeHandle, in byte[] data);
+ void setConnectTimeout(int nativeHandle, int timeout);
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl b/core/java/android/nfc/INdefTag.aidl
similarity index 78%
rename from core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl
rename to core/java/android/nfc/INdefTag.aidl
index 1f8d1a4..d131ebe 100644
--- a/core/java/com/trustedlogic/trustednfc/android/INdefTag.aidl
+++ b/core/java/android/nfc/INdefTag.aidl
@@ -14,19 +14,15 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
-import com.trustedlogic.trustednfc.android.NdefMessage;
+import android.nfc.NdefMessage;
/**
- * TODO
- *
- * {@hide}
+ * @hide
*/
interface INdefTag
{
-
NdefMessage read(int nativeHandle);
- boolean write(int nativeHandle, in NdefMessage msg);
-
+ boolean write(int nativeHandle, in NdefMessage msg);
}
\ No newline at end of file
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
new file mode 100644
index 0000000..7743ceb
--- /dev/null
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
+import android.nfc.ILlcpSocket;
+import android.nfc.ILlcpServiceSocket;
+import android.nfc.ILlcpConnectionlessSocket;
+import android.nfc.INfcTag;
+import android.nfc.IP2pTarget;
+import android.nfc.IP2pInitiator;
+
+/**
+ * @hide
+ */
+interface INfcAdapter
+{
+ ILlcpSocket getLlcpInterface();
+ ILlcpConnectionlessSocket getLlcpConnectionlessInterface();
+ ILlcpServiceSocket getLlcpServiceInterface();
+ INfcTag getNfcTagInterface();
+ IP2pTarget getP2pTargetInterface();
+ IP2pInitiator getP2pInitiatorInterface();
+
+ // NfcAdapter-class related methods
+ boolean isEnabled();
+ NdefMessage localGet();
+ void localSet(in NdefMessage message);
+ void openTagConnection(in Tag tag);
+
+ // Non-public methods
+ // TODO: check and complete
+ int createLlcpConnectionlessSocket(int sap);
+ int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength);
+ int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength);
+ int deselectSecureElement();
+ boolean disable();
+ boolean enable();
+ String getProperties(String param);
+ int[] getSecureElementList();
+ int getSelectedSecureElement();
+ int selectSecureElement(int seId);
+ int setProperties(String param, String value);
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
similarity index 62%
rename from core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl
rename to core/java/android/nfc/INfcTag.aidl
index 79543c4..2171434 100644
--- a/core/java/com/trustedlogic/trustednfc/android/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -14,25 +14,26 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
-import com.trustedlogic.trustednfc.android.NdefMessage;
+import android.nfc.NdefMessage;
/**
- * TODO
- *
- * {@hide}
+ * @hide
*/
interface INfcTag
{
-
- int close(int nativeHandle);
- int connect(int nativeHandle);
- String getType(int nativeHandle);
- byte[] getUid(int nativeHandle);
+ int close(int nativeHandle);
+ int connect(int nativeHandle);
+ String getType(int nativeHandle);
+ byte[] getUid(int nativeHandle);
boolean isNdef(int nativeHandle);
- byte[] transceive(int nativeHandle, in byte[] data);
-
+ byte[] transceive(int nativeHandle, in byte[] data);
+
+ int getLastError(int nativeHandle);
+
NdefMessage read(int nativeHandle);
- boolean write(int nativeHandle, in NdefMessage msg);
+ int write(int nativeHandle, in NdefMessage msg);
+ int makeReadOnly(int nativeHandle);
+ int getModeHint(int nativeHandle);
}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl b/core/java/android/nfc/IP2pInitiator.aidl
similarity index 72%
rename from core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
rename to core/java/android/nfc/IP2pInitiator.aidl
index 96819ae..931f1f8 100644
--- a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
+++ b/core/java/android/nfc/IP2pInitiator.aidl
@@ -14,19 +14,15 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
/**
- * TODO
- *
- * {@hide}
+ * @hide
*/
interface IP2pInitiator
{
-
- byte[] getGeneralBytes(int nativeHandle);
- int getMode(int nativeHandle);
- byte[] receive(int nativeHandle);
- boolean send(int nativeHandle, in byte[] data);
-
+ byte[] getGeneralBytes(int nativeHandle);
+ int getMode(int nativeHandle);
+ byte[] receive(int nativeHandle);
+ boolean send(int nativeHandle, in byte[] data);
}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl b/core/java/android/nfc/IP2pTarget.aidl
similarity index 69%
copy from core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
copy to core/java/android/nfc/IP2pTarget.aidl
index 96819ae..ddaaed42 100644
--- a/core/java/com/trustedlogic/trustednfc/android/IP2pInitiator.aidl
+++ b/core/java/android/nfc/IP2pTarget.aidl
@@ -14,19 +14,16 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
/**
- * TODO
- *
- * {@hide}
+ * @hide
*/
-interface IP2pInitiator
+interface IP2pTarget
{
-
- byte[] getGeneralBytes(int nativeHandle);
- int getMode(int nativeHandle);
- byte[] receive(int nativeHandle);
- boolean send(int nativeHandle, in byte[] data);
-
+ byte[] getGeneralBytes(int nativeHandle);
+ int getMode(int nativeHandle);
+ int connect(int nativeHandle);
+ boolean disconnect(int nativeHandle);
+ byte[] transceive(int nativeHandle, in byte[] data);
}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl b/core/java/android/nfc/LlcpPacket.aidl
similarity index 92%
rename from core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl
rename to core/java/android/nfc/LlcpPacket.aidl
index 297a1fe..80f424d 100644
--- a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.aidl
+++ b/core/java/android/nfc/LlcpPacket.aidl
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
-parcelable LlcpPacket;
+/**
+ * @hide
+ */
+parcelable LlcpPacket;
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java b/core/java/android/nfc/LlcpPacket.java
similarity index 71%
rename from core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java
rename to core/java/android/nfc/LlcpPacket.java
index af79023..9919dc4 100644
--- a/core/java/com/trustedlogic/trustednfc/android/LlcpPacket.java
+++ b/core/java/android/nfc/LlcpPacket.java
@@ -14,76 +14,35 @@
* limitations under the License.
*/
-/**
- * File : LLCPPacket.java
- * Original-Author : Trusted Logic S.A. (Daniel Tomas)
- * Created : 25-02-2010
- */
-
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents a LLCP packet received in a LLCP Connectionless communication;
- *
- * @since AA02.01
* @hide
*/
public class LlcpPacket implements Parcelable {
- private int mRemoteSap;
+ private final int mRemoteSap;
- private byte[] mDataBuffer;
-
- /**
- * Creator class, needed when implementing from Parcelable
- * {@hide}
- */
- public static final Parcelable.Creator<LlcpPacket> CREATOR = new Parcelable.Creator<LlcpPacket>() {
- public LlcpPacket createFromParcel(Parcel in) {
- // Remote SAP
- short sap = (short)in.readInt();
-
- // Data Buffer
- int dataLength = in.readInt();
- byte[] data = new byte[dataLength];
- in.readByteArray(data);
-
- return new LlcpPacket(sap, data);
- }
-
- public LlcpPacket[] newArray(int size) {
- return new LlcpPacket[size];
- }
- };
-
+ private final byte[] mDataBuffer;
/**
* Creates a LlcpPacket to be sent to a remote Service Access Point number
* (SAP)
- *
+ *
* @param sap Remote Service Access Point number
* @param data Data buffer
- * @since AA02.01
*/
public LlcpPacket(int sap, byte[] data) {
- mRemoteSap = sap;
+ mRemoteSap = sap;
mDataBuffer = data;
}
-
- /**
- * @hide
- */
- public LlcpPacket() {
- }
/**
* Returns the remote Service Access Point number
- *
- * @return remoteSap
- * @since AA02.01
*/
public int getRemoteSap() {
return mRemoteSap;
@@ -91,29 +50,36 @@
/**
* Returns the data buffer
- *
- * @return data
- * @since AA02.01
*/
public byte[] getDataBuffer() {
return mDataBuffer;
}
- /**
- * (Parcelable) Describe the parcel
- * {@hide}
- */
public int describeContents() {
return 0;
}
- /**
- * (Parcelable) Convert current object to a Parcel
- * {@hide}
- */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mRemoteSap);
dest.writeInt(mDataBuffer.length);
- dest.writeByteArray(mDataBuffer);
+ dest.writeByteArray(mDataBuffer);
}
-}
+
+ public static final Parcelable.Creator<LlcpPacket> CREATOR = new Parcelable.Creator<LlcpPacket>() {
+ public LlcpPacket createFromParcel(Parcel in) {
+ // Remote SAP
+ short sap = (short)in.readInt();
+
+ // Data Buffer
+ int dataLength = in.readInt();
+ byte[] data = new byte[dataLength];
+ in.readByteArray(data);
+
+ return new LlcpPacket(sap, data);
+ }
+
+ public LlcpPacket[] newArray(int size) {
+ return new LlcpPacket[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl b/core/java/android/nfc/NdefMessage.aidl
similarity index 92%
rename from core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
rename to core/java/android/nfc/NdefMessage.aidl
index e60f4e8..378b9d0 100644
--- a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
+++ b/core/java/android/nfc/NdefMessage.aidl
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package com.trustedlogic.trustednfc.android;
+
+package android.nfc;
parcelable NdefMessage;
diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java
index 557f651..fcff2c9 100644
--- a/core/java/android/nfc/NdefMessage.java
+++ b/core/java/android/nfc/NdefMessage.java
@@ -20,8 +20,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.UnsupportedOperationException;
-
/**
* NDEF Message data.
* <p>
@@ -29,23 +27,34 @@
* records.
*/
public class NdefMessage implements Parcelable {
+ private static final byte FLAG_MB = (byte) 0x80;
+ private static final byte FLAG_ME = (byte) 0x40;
+
+ private final NdefRecord[] mRecords;
+
+ //TODO(npelly) FormatException
/**
* Create an NDEF message from raw bytes.
* <p>
* Validation is performed to make sure the Record format headers are valid,
* and the ID + TYPE + PAYLOAD fields are of the correct size.
+ * @throws FormatException
*
* @hide
*/
- public NdefMessage(byte[] data) {
- throw new UnsupportedOperationException();
+ public NdefMessage(byte[] data) throws FormatException {
+ mRecords = null; // stop compiler complaints about final field
+ if (parseNdefMessage(data) == -1) {
+ throw new FormatException("Error while parsing NDEF message");
+ }
}
/**
* Create an NDEF message from NDEF records.
*/
public NdefMessage(NdefRecord[] records) {
- throw new UnsupportedOperationException();
+ mRecords = new NdefRecord[records.length];
+ System.arraycopy(records, 0, mRecords, 0, records.length);
}
/**
@@ -54,7 +63,7 @@
* @return array of zero or more NDEF records.
*/
public NdefRecord[] getRecords() {
- throw new UnsupportedOperationException();
+ return mRecords.clone();
}
/**
@@ -64,26 +73,62 @@
* @hide
*/
public byte[] toByteArray() {
- throw new UnsupportedOperationException();
+ //TODO(nxp): do not return null
+ //TODO(nxp): allocate the byte array once, copy each record once
+ //TODO(nxp): process MB and ME flags outside loop
+ if ((mRecords == null) || (mRecords.length == 0))
+ return null;
+
+ byte[] msg = {};
+
+ for (int i = 0; i < mRecords.length; i++) {
+ byte[] record = mRecords[i].toByteArray();
+ byte[] tmp = new byte[msg.length + record.length];
+
+ /* Make sure the Message Begin flag is set only for the first record */
+ if (i == 0) {
+ record[0] |= FLAG_MB;
+ } else {
+ record[0] &= ~FLAG_MB;
+ }
+
+ /* Make sure the Message End flag is set only for the last record */
+ if (i == (mRecords.length - 1)) {
+ record[0] |= FLAG_ME;
+ } else {
+ record[0] &= ~FLAG_ME;
+ }
+
+ System.arraycopy(msg, 0, tmp, 0, msg.length);
+ System.arraycopy(record, 0, tmp, msg.length, record.length);
+
+ msg = tmp;
+ }
+
+ return msg;
}
- @Override
public int describeContents() {
return 0;
}
- @Override
public void writeToParcel(Parcel dest, int flags) {
- throw new UnsupportedOperationException();
+ dest.writeInt(mRecords.length);
+ dest.writeTypedArray(mRecords, flags);
}
public static final Parcelable.Creator<NdefMessage> CREATOR =
new Parcelable.Creator<NdefMessage>() {
public NdefMessage createFromParcel(Parcel in) {
- throw new UnsupportedOperationException();
+ int recordsLength = in.readInt();
+ NdefRecord[] records = new NdefRecord[recordsLength];
+ in.readTypedArray(records, NdefRecord.CREATOR);
+ return new NdefMessage(records);
}
public NdefMessage[] newArray(int size) {
- throw new UnsupportedOperationException();
+ return new NdefMessage[size];
}
};
+
+ private native int parseNdefMessage(byte[] data);
}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl b/core/java/android/nfc/NdefRecord.aidl
similarity index 92%
rename from core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl
rename to core/java/android/nfc/NdefRecord.aidl
index 9d95174..10f89d0 100644
--- a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.aidl
+++ b/core/java/android/nfc/NdefRecord.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.trustedlogic.trustednfc.android;
+package android.nfc;
-parcelable NdefRecord;
+parcelable NdefRecord;
\ No newline at end of file
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 54cbbeb..c08f2ed 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -138,6 +138,18 @@
*/
public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
+ private static final byte FLAG_MB = (byte) 0x80;
+ private static final byte FLAG_ME = (byte) 0x40;
+ private static final byte FLAG_CF = (byte) 0x20;
+ private static final byte FLAG_SR = (byte) 0x10;
+ private static final byte FLAG_IL = (byte) 0x08;
+
+ private final byte mFlags;
+ private final short mTnf;
+ private final byte[] mType;
+ private final byte[] mId;
+ private final byte[] mPayload;
+
/**
* Construct an NDEF Record.
* <p>
@@ -153,7 +165,29 @@
* must not be null
*/
public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
- throw new UnsupportedOperationException();
+ /* check arguments */
+ if ((type == null) || (id == null) || (payload == null)) {
+ throw new IllegalArgumentException("Illegal null argument");
+ }
+
+ /* generate flag */
+ byte flags = FLAG_MB | FLAG_ME;
+
+ /* Determine if it is a short record */
+ if(payload.length < 0xFF) {
+ flags |= FLAG_SR;
+ }
+
+ /* Determine if an id is present */
+ if(id.length != 0) {
+ flags |= FLAG_IL;
+ }
+
+ mFlags = flags;
+ mTnf = tnf;
+ mType = type.clone();
+ mId = id.clone();
+ mPayload = payload.clone();
}
/**
@@ -174,7 +208,7 @@
* TNF is the top-level type.
*/
public short getTnf() {
- throw new UnsupportedOperationException();
+ return mTnf;
}
/**
@@ -184,21 +218,21 @@
* payload format.
*/
public byte[] getType() {
- throw new UnsupportedOperationException();
+ return mType.clone();
}
/**
* Returns the variable length ID.
*/
public byte[] getId() {
- throw new UnsupportedOperationException();
+ return mId.clone();
}
/**
* Returns the variable length payload.
*/
public byte[] getPayload() {
- throw new UnsupportedOperationException();
+ return mPayload.clone();
}
/**
@@ -206,26 +240,43 @@
* @hide
*/
public byte[] toByteArray() {
- throw new UnsupportedOperationException();
+ return generate(mFlags, mTnf, mType, mId, mPayload);
}
- @Override
public int describeContents() {
return 0;
}
- @Override
public void writeToParcel(Parcel dest, int flags) {
- throw new UnsupportedOperationException();
+ dest.writeInt(mTnf);
+ dest.writeInt(mType.length);
+ dest.writeByteArray(mType);
+ dest.writeInt(mId.length);
+ dest.writeByteArray(mId);
+ dest.writeInt(mPayload.length);
+ dest.writeByteArray(mPayload);
}
public static final Parcelable.Creator<NdefRecord> CREATOR =
new Parcelable.Creator<NdefRecord>() {
public NdefRecord createFromParcel(Parcel in) {
- throw new UnsupportedOperationException();
+ short tnf = (short)in.readInt();
+ int typeLength = in.readInt();
+ byte[] type = new byte[typeLength];
+ in.readByteArray(type);
+ int idLength = in.readInt();
+ byte[] id = new byte[idLength];
+ in.readByteArray(id);
+ int payloadLength = in.readInt();
+ byte[] payload = new byte[payloadLength];
+ in.readByteArray(payload);
+
+ return new NdefRecord(tnf, type, id, payload);
}
public NdefRecord[] newArray(int size) {
- throw new UnsupportedOperationException();
+ return new NdefRecord[size];
}
};
+
+ private native byte[] generate(short flags, short tnf, byte[] type, byte[] id, byte[] data);
}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl b/core/java/android/nfc/NdefTag.aidl
similarity index 89%
copy from core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
copy to core/java/android/nfc/NdefTag.aidl
index e60f4e8..288f667 100644
--- a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
+++ b/core/java/android/nfc/NdefTag.aidl
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package com.trustedlogic.trustednfc.android;
-parcelable NdefMessage;
+package android.nfc;
+
+parcelable NdefTag;
\ No newline at end of file
diff --git a/core/java/android/nfc/NdefTag.java b/core/java/android/nfc/NdefTag.java
new file mode 100644
index 0000000..25303c3
--- /dev/null
+++ b/core/java/android/nfc/NdefTag.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * NdefTag is a Tag that has NDEF messages or can store NDEF messages.
+ * <p>
+ * NDEF Tag's contain zero or more NDEF Messages in addition to the basic
+ * Tag properties of UID and Type.
+ * <p>
+ * NDEF Tag's that have been initialized will usually contain a single NDEF
+ * Message (and that Message can contain multiple NDEF Records). However it
+ * is possible for NDEF Tag's to contain multiple NDEF Messages.
+ * <p>
+ * This class is a immutable data class that contains the contents of the NDEF
+ * Message(s) as read at Tag discovery time.
+ * <p>
+ * NfcAdapter.createNdefTagConnection() can be used to modify the contents of
+ * some NDEF Tag's.
+ */
+public class NdefTag extends Tag implements Parcelable {
+ private final NdefMessage[] mMessages;
+
+ /**
+ * Hidden constructor to be used by NFC service when a
+ * tag is discovered and by Parcelable methods.
+ * @hide
+ */
+ public NdefTag(int type, byte[] uid, int nativeHandle, NdefMessage[] messages) {
+ super(type, true, uid, nativeHandle);
+ mMessages = messages.clone();
+ }
+
+ /**
+ * Get all NDEF Messages.
+ * <p>
+ * This retrieves the NDEF Messages that were found on the Tag at discovery
+ * time. It does not cause any further RF activity, and does not block.
+ * <p>
+ * Most tags only contain a single NDEF message.
+ *
+ * @return NDEF Messages found at Tag discovery
+ */
+ public NdefMessage[] getNdefMessages() {
+ return mMessages.clone();
+ }
+
+ /**
+ * Get only the NDEF Messages from a single NDEF target on a tag.
+ */
+ public NdefMessage[] getNdefMessages(String target) {
+ //TODO(nxp): new api method
+ throw new UnsupportedOperationException();
+ }
+
+ /** TODO(npelly):
+ * - check that any single tag can only have one of each NDEF type
+ * - ok to include mifare_classic?
+ */
+ public static final String TARGET_TYPE_1 = "type_1";
+ public static final String TARGET_TYPE_2 = "type_2";
+ public static final String TARGET_TYPE_3 = "type_3";
+ public static final String TARGET_TYPE_4 = "type_4";
+ public static final String TARGET_MIFARE_CLASSIC = "type_mifare_classic";
+ public static final String TARGET_OTHER = "other";
+
+ /**
+ * Return the
+ *
+ * @return
+ */
+ public String[] getNdefTargets() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mMessages.length);
+ dest.writeTypedArray(mMessages, flags);
+ }
+
+ public static final Parcelable.Creator<NdefTag> CREATOR =
+ new Parcelable.Creator<NdefTag>() {
+ public NdefTag createFromParcel(Parcel in) {
+ Tag tag = Tag.CREATOR.createFromParcel(in);
+ int messagesLength = in.readInt();
+ NdefMessage[] messages = new NdefMessage[messagesLength];
+ in.readTypedArray(messages, NdefMessage.CREATOR);
+ return new NdefTag(tag.mType, tag.mUid, tag.mNativeHandle, messages);
+ }
+ public NdefTag[] newArray(int size) {
+ return new NdefTag[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/nfc/NdefTagConnection.java b/core/java/android/nfc/NdefTagConnection.java
new file mode 100644
index 0000000..0696b37
--- /dev/null
+++ b/core/java/android/nfc/NdefTagConnection.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+import java.io.IOException;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * NdefTagConnection is a connection to an NDEF target on an NDEF tag.
+ */
+public class NdefTagConnection extends RawTagConnection {
+ public static final int NDEF_MODE_READ_ONCE = 1;
+ public static final int NDEF_MODE_READ_ONLY = 2;
+ public static final int NDEF_MODE_WRITE_ONCE = 3;
+ public static final int NDEF_MODE_WRITE_MANY = 4;
+ public static final int NDEF_MODE_UNKNOWN = 5;
+
+ private static final String TAG = "NFC";
+
+ /**
+ * Internal constructor, to be used by NfcAdapter
+ * @hide
+ */
+ NdefTagConnection(INfcAdapter service, NdefTag tag) throws RemoteException {
+ super(service, tag);
+ }
+
+ /**
+ * Read NDEF message(s).
+ * This will always return the most up to date payload, and can block.
+ * It can be canceled with close().
+ * Most NDEF tags will contain just one NDEF message.
+ * <p>
+ * @throws FormatException if the tag is not NDEF formatted
+ * @throws IOException if the target is lost or connection closed
+ * @throws FormatException
+ */
+ public NdefMessage[] readNdefMessages() throws IOException, FormatException {
+ //TODO(nxp): do not use getLastError(), it is racy
+ try {
+ NdefMessage[] msgArray = new NdefMessage[1];
+ NdefMessage msg = mTagService.read(mTag.mNativeHandle);
+ if (msg == null) {
+ int errorCode = mTagService.getLastError(mTag.mNativeHandle);
+ switch (errorCode) {
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ throw new FormatException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ }
+ msgArray[0] = msg;
+ return msgArray;
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died");
+ return null;
+ }
+ }
+
+ /**
+ * Attempt to write an NDEF message to a tag.
+ * This method will block until the data is written. It can be canceled
+ * with close().
+ * Many tags are write-once, so use this method carefully.
+ * Specification allows for multiple NDEF messages per NDEF tag, but it is
+ * encourage to only write one message, this so API only takes a single
+ * message. Use NdefRecord to write several records to a single tag.
+ * For write-many tags, use makeReadOnly() after this method to attempt
+ * to prevent further modification. For write-once tags this is not
+ * neccesary.
+ * Requires NFC_WRITE permission.
+ * @throws FormatException if the tag is not suitable for NDEF messages
+ * @throws IOException if the target is lost or connection closed or the
+ * write failed
+ */
+ public void writeNdefMessage(NdefMessage message) throws IOException, FormatException {
+ try {
+ int errorCode = mTagService.write(mTag.mNativeHandle, message);
+ switch (errorCode) {
+ case ErrorCodes.SUCCESS:
+ break;
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ throw new FormatException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died");
+ }
+ }
+
+ /**
+ * Attempts to make the NDEF data in this tag read-only.
+ * This method will block until the action is complete. It can be canceled
+ * with close().
+ * Requires NFC_WRITE permission.
+ * @return true if the tag is now read-only
+ * @throws IOException if the target is lost, or connection closed
+ */
+ public boolean makeReadOnly() throws IOException {
+ try {
+ int errorCode = mTagService.makeReadOnly(mTag.mNativeHandle);
+ switch (errorCode) {
+ case ErrorCodes.SUCCESS:
+ return true;
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ return false;
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died");
+ return false;
+ }
+ }
+
+ /**
+ * Read/Write mode hint.
+ * Provides a hint if further reads or writes are likely to suceed.
+ * @return one of NDEF_MODE
+ * @throws IOException if the target is lost or connection closed
+ */
+ public int getModeHint() throws IOException {
+ try {
+ int result = mTagService.getModeHint(mTag.mNativeHandle);
+ if (ErrorCodes.isError(result)) {
+ switch (result) {
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ }
+ return result;
+
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died");
+ return NDEF_MODE_UNKNOWN;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
new file mode 100644
index 0000000..02b9fb71
--- /dev/null
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+import java.lang.UnsupportedOperationException;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.nfc.INfcAdapter;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+//TODO(npelly) permission {@link android.Manifest.permission#NFC_MODIFY}
+/**
+ * Represents a local NFC Adapter.
+ * <p>
+ * Use the static {@link #getDefaultAdapter} method to get the default NFC
+ * Adapter for this Android device. Most Android devices will have only one NFC
+ * Adapter, and {@link #getDefaultAdapter} returns the singleton object.
+ * <p>
+ * {@link NfcAdapter} can be used to create {@link RawTagConnection} or
+ * {@link NdefTagConnection} connections to modify or perform low level access
+ * to NFC Tags.
+ * <p class="note">
+ * <strong>Note:</strong> Some methods require the
+ * TODO permission.
+ */
+public final class NfcAdapter {
+ /**
+ * Intent to start an activity when a non-NDEF tag is discovered.
+ * TODO(npelly) finalize decision on using CATEGORY or DATA URI to provide a
+ * hint for applications to filter the tag type.
+ * TODO(npelly) probably combine these two intents since tags aren't that simple
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
+
+ /**
+ * Intent to start an activity when a NDEF tag is discovered. TODO(npelly)
+ * finalize decision on using CATEGORY or DATA URI to provide a hint for
+ * applications to filter the tag type.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NDEF_TAG_DISCOVERED =
+ "android.nfc.action.NDEF_TAG_DISCOVERED";
+
+ /**
+ * Mandatory Tag extra for the ACTION_TAG and ACTION_NDEF_TAG intents.
+ */
+ public static final String EXTRA_TAG = "android.nfc.extra.TAG";
+
+ /**
+ * Broadcast Action: a transaction with a secure element has been detected.
+ * <p>
+ * Always contains the extra field
+ * {@link android.nfc.NfcAdapter#EXTRA_AID}
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TRANSACTION_DETECTED =
+ "android.nfc.action.TRANSACTION_DETECTED";
+
+ /**
+ * Mandatory byte array extra field in
+ * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}.
+ * <p>
+ * Contains the AID of the applet involved in the transaction.
+ * @hide
+ */
+ public static final String EXTRA_AID = "android.nfc.extra.AID";
+
+ /**
+ * LLCP link status: The LLCP link is activated.
+ * @hide
+ */
+ public static final int LLCP_LINK_STATE_ACTIVATED = 0;
+
+ /**
+ * LLCP link status: The LLCP link is deactivated.
+ * @hide
+ */
+ public static final int LLCP_LINK_STATE_DEACTIVATED = 1;
+
+ /**
+ * Broadcast Action: the LLCP link state changed.
+ * <p>
+ * Always contains the extra field
+ * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_LLCP_LINK_STATE_CHANGED =
+ "android.nfc.action.LLCP_LINK_STATE_CHANGED";
+
+ /**
+ * Used as int extra field in
+ * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}.
+ * <p>
+ * It contains the new state of the LLCP link.
+ * @hide
+ */
+ public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE";
+
+ /**
+ * Tag Reader Discovery mode
+ * @hide
+ */
+ private static final int DISCOVERY_MODE_TAG_READER = 0;
+
+ /**
+ * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an
+ * NFC-IP1 communication. Implementations should not assume that the
+ * controller will end up behaving as an NFC-IP1 target or initiator and
+ * should handle both cases, depending on the type of the remote peer type.
+ * @hide
+ */
+ private static final int DISCOVERY_MODE_NFCIP1 = 1;
+
+ /**
+ * Card Emulation mode Enables the manager to act as an NFC tag. Provided
+ * that a Secure Element (an UICC for instance) is connected to the NFC
+ * controller through its SWP interface, it can be exposed to the outside
+ * NFC world and be addressed by external readers the same way they would
+ * with a tag.
+ * <p>
+ * Which Secure Element is exposed is implementation-dependent.
+ *
+ * @hide
+ */
+ private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
+
+ private static final String TAG = "NFC";
+
+ private static boolean sIsInitialized = false;
+ private static NfcAdapter sAdapter;
+
+ private final INfcAdapter mService;
+
+ private NfcAdapter(INfcAdapter service) {
+ mService = service;
+ }
+
+ /**
+ * Get a handle to the default NFC Adapter on this Android device.
+ * <p>
+ * Most Android devices will only have one NFC Adapter (NFC Controller).
+ *
+ * @return the default NFC adapter, or null if no NFC adapter exists
+ */
+ public static NfcAdapter getDefaultAdapter() {
+ synchronized (NfcAdapter.class) {
+ if (sIsInitialized) {
+ return sAdapter;
+ }
+ sIsInitialized = true;
+
+ // TODO(npelly): check which method to use here to get the service
+ IBinder b = ServiceManager.getService(Context.NFC_SERVICE);
+ if (b == null) {
+ return null; // This device does not have NFC
+ }
+
+ sAdapter = new NfcAdapter(INfcAdapter.Stub.asInterface(b));
+ return sAdapter;
+ }
+ }
+
+ /**
+ * Return true if this NFC Adapter is enabled to discover new tags.
+ * <p>
+ * If this method returns false, then applications should request the user
+ * turn on NFC tag discovery in Settings.
+ *
+ * @return true if this NFC Adapter is enabled to discover new tags
+ */
+ public boolean isTagDiscoveryEnabled() {
+ try {
+ return mService.isEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in isEnabled()", e);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean enableTagDiscovery() {
+ try {
+ return mService.enable();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in enable()", e);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean disableTagDiscovery() {
+ try {
+ return mService.disable();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in disable()", e);
+ return false;
+ }
+ }
+
+ /**
+ * Set the NDEF Message that this NFC adapter should appear as to Tag
+ * readers.
+ * <p>
+ * Any Tag reader can read the contents of the local tag when it is in
+ * proximity, without any further user confirmation.
+ * <p>
+ * The implementation of this method must either
+ * <ul>
+ * <li>act as a passive tag containing this NDEF message
+ * <li>provide the NDEF message on over LLCP to peer NFC adapters
+ * </ul>
+ * The NDEF message is preserved across reboot.
+ * <p>
+ * Requires NFC_WRITE permission
+ *
+ * @param message NDEF message to make public
+ */
+ public void setLocalNdefMessage(NdefMessage message) {
+ try {
+ mService.localSet(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died", e);
+ }
+ }
+
+ /**
+ * Get the NDEF Message that this adapter appears as to Tag readers.
+ * <p>
+ * Requires NFC_WRITE permission
+ *
+ * @return NDEF Message that is publicly readable
+ */
+ public NdefMessage getLocalNdefMessage() {
+ try {
+ return mService.localGet();
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died", e);
+ return null;
+ }
+ }
+
+ /**
+ * Create a raw tag connection to the default Target
+ */
+ public RawTagConnection createRawTagConnection(Tag tag) {
+ try {
+ return new RawTagConnection(mService, tag);
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died", e);
+ return null;
+ }
+ }
+
+ /**
+ * Create a raw tag connection to the specified Target
+ */
+ public RawTagConnection createRawTagConnection(Tag tag, String target) {
+ //TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Create an NDEF tag connection to the default Target
+ */
+ public NdefTagConnection createNdefTagConnection(NdefTag tag) {
+ try {
+ return new NdefTagConnection(mService, tag);
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died", e);
+ return null;
+ }
+ }
+
+ /**
+ * Create an NDEF tag connection to the specified Target
+ */
+ public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) {
+ //TODO
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/java/android/nfc/RawTagConnection.java b/core/java/android/nfc/RawTagConnection.java
new file mode 100644
index 0000000..67a836f
--- /dev/null
+++ b/core/java/android/nfc/RawTagConnection.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+import java.io.IOException;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * RawTagConnection is a low-level connection to a Tag.
+ * <p>
+ * The only data transfer method that TagConnection offers is transceive().
+ * Applications must implement there own protocol stack on top of transceive().
+ * <p>
+ * Use NfcAdapter.createRawTagConnection() to create a RawTagConnection object.
+ *
+ * * <p class="note"><strong>Note:</strong>
+ * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
+ * permission and some also require the
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+
+ */
+public class RawTagConnection {
+
+ /*package*/ final INfcAdapter mService;
+ /*package*/ final INfcTag mTagService;
+ /*package*/ final Tag mTag;
+ /*package*/ boolean mIsConnected;
+
+ private static final String TAG = "NFC";
+
+ /* package private */ RawTagConnection(INfcAdapter service, Tag tag) throws RemoteException {
+ mService = service;
+ mTagService = service.getNfcTagInterface();
+ mService.openTagConnection(tag); // TODO(nxp): don't connect until connect()
+ mTag = tag;
+ }
+
+ /**
+ * Get the Tag this connection is associated with.
+ */
+ public Tag getTag() {
+ return mTag;
+ }
+
+ public String getTagTarget() {
+ //TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Helper to indicate if transceive() calls might succeed.
+ * <p>
+ * Does not cause RF activity, and does not block.
+ * <p>
+ * Returns true if connect() has completed successfully, and the Tag is not
+ * yet known to be out of range. Applications must still handle IOException
+ * while using transceive().
+ */
+ public boolean isConnected() {
+ // TODO(nxp): update mIsConnected when tag goes out of range -
+ // but do not do an active prescence check in
+ // isConnected()
+ return mIsConnected;
+ }
+
+ /**
+ * Connect to tag.
+ * <p>
+ * This method blocks until the connection is established.
+ * <p>
+ * close() can be called from another thread to cancel this connection
+ * attempt.
+ *
+ * @throws IOException if the target is lost, or connect canceled
+ */
+ public void connect() throws IOException {
+ //TODO(nxp): enforce exclusivity
+ mIsConnected = true;
+ }
+
+ /**
+ * Close tag connection.
+ * <p>
+ * Causes blocking operations such as transceive() or connect() to
+ * be canceled and immediately throw IOException.
+ * <p>
+ * This object cannot be re-used after calling close(). Further calls
+ * to transceive() or connect() will fail.
+ */
+ public void close() {
+ mIsConnected = false;
+ try {
+ mTagService.close(mTag.mNativeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died", e);
+ }
+ }
+
+ /**
+ * Send data to a tag, and return the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with close().
+ * <p>
+ * Requires NFC_WRITE permission.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ try {
+ byte[] response = mTagService.transceive(mTag.mNativeHandle, data);
+ if (response == null) {
+ throw new IOException("transcieve failed");
+ }
+ return response;
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service died", e);
+ throw new IOException("NFC service died");
+ }
+ }
+}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl b/core/java/android/nfc/Tag.aidl
similarity index 89%
copy from core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
copy to core/java/android/nfc/Tag.aidl
index e60f4e8..312261e 100644
--- a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.aidl
+++ b/core/java/android/nfc/Tag.aidl
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package com.trustedlogic.trustednfc.android;
-parcelable NdefMessage;
+package android.nfc;
+
+parcelable Tag;
\ No newline at end of file
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
new file mode 100644
index 0000000..8f731e7
--- /dev/null
+++ b/core/java/android/nfc/Tag.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2010 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.nfc;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Immutable data class, represents a discovered tag.
+ * <p>
+ * A tag is a passive NFC element, such as NFC Forum Tag's, Mifare class Tags,
+ * Sony Felica Tags.
+ * <p>
+ * Tag's have a type and usually have a UID.
+ * <p>
+ * Tag objects are passed to applications via the NfcAdapter.EXTRA_TAG extra
+ * in NfcAdapter.ACTION_TAG_DISCOVERED intents. The Tag object is immutable
+ * and represents the state of the Tag at the time of discovery. It can be
+ * directly queried for its UID and Type, or used to create a TagConnection
+ * (NfcAdapter.createTagConnection()).
+ * <p>
+ * This Tag object can only be used to create a TagConnection while it is in
+ * range. If it is removed and then returned to range then the most recent
+ * Tag object (in ACTION_TAG_DISCOVERED) should be used to create a
+ * TagConnection.
+ */
+public class Tag implements Parcelable {
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO14443_A = 1; /* phNfc_eISO14443_A_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO14443_4A = 2; /* phNfc_eISO14443_4A_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO14443_3A = 3; /* phNfc_eISO14443_3A_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_MIFARE = 4; /* phNfc_eMifare_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO14443_B = 5; /* phNfc_eISO14443_B_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO14443_4B = 6; /* phNfc_eISO14443_4B_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO14443_B_PRIME = 7; /* phNfc_eISO14443_BPrime_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_FELICA = 8; /* phNfc_eFelica_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_JEWEL = 9; /* phNfc_eJewel_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_ISO15693 = 10; /* phNfc_eISO15693_PICC */
+
+ /**
+ * @hide
+ */
+ public static final int NFC_TAG_OTHER = 11; /* phNfc_ePICC_DevType */
+
+
+ public static final String TARGET_ISO_14443_3A = "iso14443_3a";
+
+ public static final String TARGET_ISO_14443_3B = "iso14443_3b";
+
+ public static final String TARGET_ISO_14443_3B_PRIME = "iso14443_3b";
+
+ public static final String TARGET_ISO_14443_4 = "iso14443_4";
+
+ public static final String TARGET_ISO_15693 = "iso15693";
+
+ public static final String TARGET_JIS_X_6319_4 = "jis_x_6319_4";
+
+ public static final String TARGET_TOPAZ = "topaz";
+
+ public static final String TARGET_OTHER = "other";
+
+ /*package*/ final int mType;
+ /*package*/ final boolean mIsNdef;
+ /*package*/ final byte[] mUid;
+ /*package*/ final int mNativeHandle;
+
+ /**
+ * Hidden constructor to be used by NFC service only.
+ * @hide
+ */
+ public Tag(int type, boolean isNdef, byte[] uid, int nativeHandle) {
+ mType = type;
+ mIsNdef = isNdef;
+ mUid = uid.clone();
+ mNativeHandle = nativeHandle;
+ }
+
+ /**
+ * For use by NfcService only.
+ * @hide
+ */
+ public int getHandle() {
+ return mNativeHandle;
+ }
+
+ /**
+ * Return the available targets that this NFC adapter can use to create
+ * a RawTagConnection.
+ *
+ * @return
+ */
+ public String[] getRawTargets() {
+ //TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get the Tag type.
+ * <p>
+ * The Tag type is one of the NFC_TAG constants. It is read at discovery
+ * time and this method does not cause any further RF activity and does not
+ * block.
+ *
+ * @return a NFC_TAG constant
+ * @hide
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Get the Tag Identifier (if it has one).
+ * <p>
+ * Tag ID is usually a serial number for the tag.
+ * <p>
+ * The Tag ID is read at discovery time and this method does not cause any
+ * further RF activity and does not block.
+ *
+ * @return ID, or null if it does not exist
+ */
+ public byte[] getId() {
+ if (mUid.length > 0) {
+ return mUid.clone();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ boolean[] booleans = new boolean[] {mIsNdef};
+ dest.writeInt(mType);
+ dest.writeBooleanArray(booleans);
+ dest.writeInt(mUid.length);
+ dest.writeByteArray(mUid);
+ dest.writeInt(mNativeHandle);
+ }
+
+ public static final Parcelable.Creator<Tag> CREATOR =
+ new Parcelable.Creator<Tag>() {
+ public Tag createFromParcel(Parcel in) {
+ boolean[] booleans = new boolean[1];
+ int type = in.readInt();
+ in.readBooleanArray(booleans);
+ boolean isNdef = booleans[0];
+ int uidLength = in.readInt();
+ byte[] uid = new byte[uidLength];
+ in.readByteArray(uid);
+ int nativeHandle = in.readInt();
+
+ return new Tag(type, isNdef, uid, nativeHandle);
+ }
+ public Tag[] newArray(int size) {
+ return new Tag[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index c185007..de5b7b9 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -38,7 +38,10 @@
* network access on the application's main thread, where UI
* operations are received and animations take place. Keeping disk
* and network operations off the main thread makes for much smoother,
- * more responsive applications.
+ * more responsive applications. By keeping your application's main thread
+ * responsive, you also prevent
+ * <a href="{@docRoot}guide/practices/design/responsiveness.html">ANR dialogs</a>
+ * from being shown to users.
*
* <p class="note">Note that even though an Android device's disk is
* often on flash memory, many devices run a filesystem on top of that
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 60ea95c..467a0ac 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -484,7 +484,7 @@
* IObbActionListener to inform it of the terminal state of the
* call.
*/
- public void mountObb(String filename, String key, IObbActionListener token)
+ public void mountObb(String filename, String key, IObbActionListener token, int nonce)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -493,6 +493,7 @@
_data.writeString(filename);
_data.writeString(key);
_data.writeStrongBinder((token != null ? token.asBinder() : null));
+ _data.writeInt(nonce);
mRemote.transact(Stub.TRANSACTION_mountObb, _data, _reply, 0);
_reply.readException();
} finally {
@@ -508,8 +509,8 @@
* IObbActionListener to inform it of the terminal state of the
* call.
*/
- public void unmountObb(String filename, boolean force, IObbActionListener token)
- throws RemoteException {
+ public void unmountObb(String filename, boolean force, IObbActionListener token,
+ int nonce) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
@@ -517,6 +518,7 @@
_data.writeString(filename);
_data.writeInt((force ? 1 : 0));
_data.writeStrongBinder((token != null ? token.asBinder() : null));
+ _data.writeInt(nonce);
mRemote.transact(Stub.TRANSACTION_unmountObb, _data, _reply, 0);
_reply.readException();
} finally {
@@ -855,7 +857,9 @@
key = data.readString();
IObbActionListener observer;
observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
- mountObb(filename, key, observer);
+ int nonce;
+ nonce = data.readInt();
+ mountObb(filename, key, observer, nonce);
reply.writeNoException();
return true;
}
@@ -867,7 +871,9 @@
force = 0 != data.readInt();
IObbActionListener observer;
observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
- unmountObb(filename, force, observer);
+ int nonce;
+ nonce = data.readInt();
+ unmountObb(filename, force, observer, nonce);
reply.writeNoException();
return true;
}
@@ -979,7 +985,7 @@
* MountService will call back to the supplied IObbActionListener to inform
* it of the terminal state of the call.
*/
- public void mountObb(String filename, String key, IObbActionListener token)
+ public void mountObb(String filename, String key, IObbActionListener token, int nonce)
throws RemoteException;
/*
@@ -1023,7 +1029,7 @@
* MountService will call back to the supplied IObbActionListener to inform
* it of the terminal state of the call.
*/
- public void unmountObb(String filename, boolean force, IObbActionListener token)
+ public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
throws RemoteException;
/*
diff --git a/core/java/android/os/storage/IObbActionListener.java b/core/java/android/os/storage/IObbActionListener.java
index 2c098ac..d6fa58a 100644
--- a/core/java/android/os/storage/IObbActionListener.java
+++ b/core/java/android/os/storage/IObbActionListener.java
@@ -69,9 +69,11 @@
data.enforceInterface(DESCRIPTOR);
String filename;
filename = data.readString();
- String status;
- status = data.readString();
- this.onObbResult(filename, status);
+ int nonce;
+ nonce = data.readInt();
+ int status;
+ status = data.readInt();
+ this.onObbResult(filename, nonce, status);
reply.writeNoException();
return true;
}
@@ -101,13 +103,15 @@
* on
* @param returnCode status of the operation
*/
- public void onObbResult(String filename, String status) throws RemoteException {
+ public void onObbResult(String filename, int nonce, int status)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(filename);
- _data.writeString(status);
+ _data.writeInt(nonce);
+ _data.writeInt(status);
mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, 0);
_reply.readException();
} finally {
@@ -124,7 +128,8 @@
* Return from an OBB action result.
*
* @param filename the path to the OBB the operation was performed on
- * @param returnCode status of the operation
+ * @param nonce identifier that is meaningful to the receiver
+ * @param status status code as defined in {@link OnObbStateChangeListener}
*/
- public void onObbResult(String filename, String status) throws RemoteException;
+ public void onObbResult(String filename, int nonce, int status) throws RemoteException;
}
diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java
index a2d0a56..950195b 100644
--- a/core/java/android/os/storage/OnObbStateChangeListener.java
+++ b/core/java/android/os/storage/OnObbStateChangeListener.java
@@ -17,15 +17,69 @@
package android.os.storage;
/**
- * Used for receiving notifications from {@link StorageManager}.
+ * Used for receiving notifications from {@link StorageManager} about OBB file
+ * states.
*/
public abstract class OnObbStateChangeListener {
+
+ /**
+ * The OBB container is now mounted and ready for use. Returned in status
+ * messages from calls made via {@link StorageManager}
+ */
+ public static final int MOUNTED = 1;
+
+ /**
+ * The OBB container is now unmounted and not usable. Returned in status
+ * messages from calls made via {@link StorageManager}
+ */
+ public static final int UNMOUNTED = 2;
+
+ /**
+ * There was an internal system error encountered while trying to mount the
+ * OBB. Returned in status messages from calls made via
+ * {@link StorageManager}
+ */
+ public static final int ERROR_INTERNAL = 20;
+
+ /**
+ * The OBB could not be mounted by the system. Returned in status messages
+ * from calls made via {@link StorageManager}
+ */
+ public static final int ERROR_COULD_NOT_MOUNT = 21;
+
+ /**
+ * The OBB could not be unmounted. This most likely indicates that a file is
+ * in use on the OBB. Returned in status messages from calls made via
+ * {@link StorageManager}
+ */
+ public static final int ERROR_COULD_NOT_UNMOUNT = 22;
+
+ /**
+ * A call was made to unmount the OBB when it was not mounted. Returned in
+ * status messages from calls made via {@link StorageManager}
+ */
+ public static final int ERROR_NOT_MOUNTED = 23;
+
+ /**
+ * The OBB has already been mounted. Returned in status messages from calls
+ * made via {@link StorageManager}
+ */
+ public static final int ERROR_ALREADY_MOUNTED = 24;
+
+ /**
+ * The current application does not have permission to use this OBB because
+ * the OBB indicates it's owned by a different package or the key used to
+ * open it is incorrect. Returned in status messages from calls made via
+ * {@link StorageManager}
+ */
+ public static final int ERROR_PERMISSION_DENIED = 25;
+
/**
* Called when an OBB has changed states.
*
* @param path path to the OBB file the state change has happened on
* @param state the current state of the OBB
*/
- public void onObbStateChange(String path, String state) {
+ public void onObbStateChange(String path, int state) {
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2ebd049..73ac79f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -22,12 +22,13 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* StorageManager is the interface to the systems storage service. The storage
@@ -69,7 +70,12 @@
/*
* List of our listeners
*/
- private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
+ private List<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
+
+ /*
+ * Next available nonce
+ */
+ final private AtomicInteger mNextNonce = new AtomicInteger(0);
private class MountServiceBinderListener extends IMountServiceListener.Stub {
public void onUsbMassStorageConnectionChanged(boolean available) {
@@ -93,57 +99,38 @@
private final ObbActionListener mObbActionListener = new ObbActionListener();
private class ObbActionListener extends IObbActionListener.Stub {
- private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
+ private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
@Override
- public void onObbResult(String filename, String status) throws RemoteException {
+ public void onObbResult(String filename, int nonce, int status) throws RemoteException {
+ final ObbListenerDelegate delegate;
synchronized (mListeners) {
- final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<ObbListenerDelegate> ref = iter.next();
-
- final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
- if (delegate == null) {
- iter.remove();
- continue;
- }
-
- delegate.sendObbStateChanged(filename, status);
+ delegate = mListeners.get(nonce);
+ if (delegate != null) {
+ mListeners.remove(nonce);
}
}
+
+ if (delegate != null) {
+ delegate.sendObbStateChanged(filename, status);
+ }
}
- public void addListener(OnObbStateChangeListener listener) {
- if (listener == null) {
- return;
- }
+ public int addListener(OnObbStateChangeListener listener) {
+ final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
synchronized (mListeners) {
- final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<ObbListenerDelegate> ref = iter.next();
-
- final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
- if (delegate == null) {
- iter.remove();
- continue;
- }
-
- /*
- * If we're already in the listeners, we don't need to be in
- * there again.
- */
- if (listener.equals(delegate.getListener())) {
- return;
- }
- }
-
- final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
- mListeners.add(new WeakReference<ObbListenerDelegate>(delegate));
+ mListeners.put(delegate.nonce, delegate);
}
+
+ return delegate.nonce;
}
}
+ private int getNextNonce() {
+ return mNextNonce.getAndIncrement();
+ }
+
/**
* Private class containing sender and receiver code for StorageEvents.
*/
@@ -151,7 +138,10 @@
private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
private final Handler mHandler;
+ private final int nonce;
+
ObbListenerDelegate(OnObbStateChangeListener listener) {
+ nonce = getNextNonce();
mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
mHandler = new Handler(mTgtLooper) {
@Override
@@ -180,7 +170,7 @@
return mObbEventListenerRef.get();
}
- void sendObbStateChanged(String path, String state) {
+ void sendObbStateChanged(String path, int state) {
ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state);
mHandler.sendMessage(e.getMessage());
}
@@ -191,9 +181,10 @@
*/
private class ObbStateChangedStorageEvent extends StorageEvent {
public final String path;
- public final String state;
- public ObbStateChangedStorageEvent(String path, String state) {
+ public final int state;
+
+ public ObbStateChangedStorageEvent(String path, int state) {
super(EVENT_OBB_STATE_CHANGED);
this.path = path;
this.state = state;
@@ -420,10 +411,8 @@
* <p>
* The OBB will remain mounted for as long as the StorageManager reference
* is held by the application. As soon as this reference is lost, the OBBs
- * in use will be unmounted. The {@link OnObbStateChangeListener} registered with
- * this call will receive all further OBB-related events until it goes out
- * of scope. If the caller is not interested in whether the call succeeds,
- * the <code>listener</code> may be specified as <code>null</code>.
+ * in use will be unmounted. The {@link OnObbStateChangeListener} registered
+ * with this call will receive the success or failure of this operation.
* <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the
* file matches a package ID that is owned by the calling program's UID.
@@ -433,12 +422,21 @@
* @param filename the path to the OBB file
* @param key secret used to encrypt the OBB; may be <code>null</code> if no
* encryption was used on the OBB.
+ * @param listener will receive the success or failure of the operation
* @return whether the mount call was successfully queued or not
*/
public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+
try {
- mObbActionListener.addListener(listener);
- mMountService.mountObb(filename, key, mObbActionListener);
+ final int nonce = mObbActionListener.addListener(listener);
+ mMountService.mountObb(filename, key, mObbActionListener, nonce);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
@@ -452,10 +450,8 @@
* <code>force</code> flag is true, it will kill any application needed to
* unmount the given OBB (even the calling application).
* <p>
- * The {@link OnObbStateChangeListener} registered with this call will receive all
- * further OBB-related events until it goes out of scope. If the caller is
- * not interested in whether the call succeeded, the listener may be
- * specified as <code>null</code>.
+ * The {@link OnObbStateChangeListener} registered with this call will
+ * receive the success or failure of this operation.
* <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the
* file matches a package ID that is owned by the calling program's UID.
@@ -466,12 +462,21 @@
* @param filename path to the OBB file
* @param force whether to kill any programs using this in order to unmount
* it
+ * @param listener will receive the success or failure of the operation
* @return whether the unmount call was successfully queued or not
*/
public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+
try {
- mObbActionListener.addListener(listener);
- mMountService.unmountObb(filename, force, mObbActionListener);
+ final int nonce = mObbActionListener.addListener(listener);
+ mMountService.unmountObb(filename, force, mObbActionListener, nonce);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
@@ -486,7 +491,11 @@
* @param filename path to OBB image
* @return true if OBB is mounted; false if not mounted or on error
*/
- public boolean isObbMounted(String filename) throws IllegalArgumentException {
+ public boolean isObbMounted(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
try {
return mMountService.isObbMounted(filename);
} catch (RemoteException e) {
@@ -506,12 +515,14 @@
* not mounted or exception encountered trying to read status
*/
public String getMountedObbPath(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
try {
return mMountService.getMountedObbPath(filename);
} catch (RemoteException e) {
Log.e(TAG, "Failed to find mounted path for OBB", e);
- } catch (IllegalArgumentException e) {
- Log.d(TAG, "Couldn't read OBB file", e);
}
return null;
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index bf9e854..6d8bd9b 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1205,9 +1205,8 @@
}
Uri uri = uriBuilder.build();
- if (DEBUG) {
- Log.v(TAG, "getOrCreateThreadId uri: " + uri);
- }
+ //if (DEBUG) Log.v(TAG, "getOrCreateThreadId uri: " + uri);
+
Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
uri, ID_PROJECTION, null, null, null);
if (DEBUG) {
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index a52a221..5cbfe74 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -71,7 +71,6 @@
private final BluetoothService mBluetoothService;
private final BluetoothAdapter mAdapter;
private int mTargetA2dpState;
- private boolean mAdjustedPriority = false;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -326,7 +325,10 @@
String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
- // State is DISCONNECTED
+ // State is DISCONNECTED and we are connecting.
+ if (getSinkPriority(device) < BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
+ setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
+ }
handleSinkStateChange(device, state, BluetoothA2dp.STATE_CONNECTING);
if (!connectSinkNative(path)) {
@@ -491,14 +493,10 @@
mTargetA2dpState = -1;
if (getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
- state == BluetoothA2dp.STATE_CONNECTING ||
state == BluetoothA2dp.STATE_CONNECTED) {
// We have connected or attempting to connect.
// Bump priority
setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
- }
-
- if (state == BluetoothA2dp.STATE_CONNECTED) {
// We will only have 1 device with AUTO_CONNECT priority
// To be backward compatible set everyone else to have PRIORITY_ON
adjustOtherSinkPriorities(device);
@@ -515,14 +513,11 @@
}
private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) {
- if (!mAdjustedPriority) {
- for (BluetoothDevice device : mAdapter.getBondedDevices()) {
- if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
- !device.equals(connectedDevice)) {
- setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
- }
+ for (BluetoothDevice device : mAdapter.getBondedDevices()) {
+ if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+ !device.equals(connectedDevice)) {
+ setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
}
- mAdjustedPriority = true;
}
}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 19abec1..e82ed9f 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -519,6 +519,7 @@
return false;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ super.onTouchEvent(event);
if (mHasPerformedLongClick) {
mGotTouchDown = false;
return false;
@@ -684,9 +685,6 @@
// webkit's drawing.
setWillNotDraw(!inPassword);
setBackgroundDrawable(inPassword ? mBackground : null);
- // For non-password fields, avoid the invals from TextView's blinking
- // cursor
- setCursorVisible(inPassword);
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6df5445..84aca60 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5039,6 +5039,8 @@
if (!mAllowPanAndScale) {
return true;
}
+ mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
}
x = mScaleDetector.getFocusX();
y = mScaleDetector.getFocusY();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5be52c4..6dcf4e6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -92,6 +92,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.view.ViewRoot;
@@ -6852,8 +6853,17 @@
}
private void prepareCursorControllers() {
+ boolean windowSupportsHandles = false;
+
+ ViewGroup.LayoutParams params = getRootView().getLayoutParams();
+ if (params instanceof WindowManager.LayoutParams) {
+ WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
+ windowSupportsHandles = windowParams.type < WindowManager.LayoutParams.FIRST_SUB_WINDOW
+ || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW;
+ }
+
// TODO Add an extra android:cursorController flag to disable the controller?
- if (mCursorVisible && mLayout != null) {
+ if (windowSupportsHandles && mCursorVisible && mLayout != null) {
if (mInsertionPointCursorController == null) {
mInsertionPointCursorController = new InsertionPointCursorController();
}
@@ -6861,7 +6871,7 @@
mInsertionPointCursorController = null;
}
- if (textCanBeSelected() && mLayout != null) {
+ if (windowSupportsHandles && textCanBeSelected() && mLayout != null) {
if (mSelectionModifierCursorController == null) {
mSelectionModifierCursorController = new SelectionModifierCursorController();
}
diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl
deleted file mode 100644
index 35746ad..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/ILlcpConnectionlessSocket.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import com.trustedlogic.trustednfc.android.LlcpPacket;
-
-/**
- * TODO
- *
- * {@hide}
- */
-interface ILlcpConnectionlessSocket
-{
-
- void close(int nativeHandle);
- int getSap(int nativeHandle);
- LlcpPacket receiveFrom(int nativeHandle);
- int sendTo(int nativeHandle, in LlcpPacket packet);
-
-}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl b/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl
deleted file mode 100644
index e9169d8..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/ILlcpSocket.aidl
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.trustedlogic.trustednfc.android;
-
-/**
- * TODO
- *
- * {@hide}
- */
-interface ILlcpSocket
-{
-
- int close(int nativeHandle);
- int connect(int nativeHandle, int sap);
- int connectByName(int nativeHandle, String sn);
- int getConnectTimeout(int nativeHandle);
- int getLocalSap(int nativeHandle);
- int getLocalSocketMiu(int nativeHandle);
- int getLocalSocketRw(int nativeHandle);
- int getRemoteSocketMiu(int nativeHandle);
- int getRemoteSocketRw(int nativeHandle);
- int receive(int nativeHandle, out byte[] receiveBuffer);
- int send(int nativeHandle, in byte[] data);
- void setConnectTimeout(int nativeHandle, int timeout);
-
-}
-
diff --git a/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl b/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl
deleted file mode 100644
index ce36ab2..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/INfcManager.aidl
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import com.trustedlogic.trustednfc.android.ILlcpSocket;
-import com.trustedlogic.trustednfc.android.ILlcpServiceSocket;
-import com.trustedlogic.trustednfc.android.ILlcpConnectionlessSocket;
-import com.trustedlogic.trustednfc.android.INfcTag;
-import com.trustedlogic.trustednfc.android.IP2pTarget;
-import com.trustedlogic.trustednfc.android.IP2pInitiator;
-
-
-/**
- * Interface that allows controlling NFC activity.
- *
- * {@hide}
- */
-interface INfcManager
-{
-
- ILlcpSocket getLlcpInterface();
- ILlcpConnectionlessSocket getLlcpConnectionlessInterface();
- ILlcpServiceSocket getLlcpServiceInterface();
- INfcTag getNfcTagInterface();
- IP2pTarget getP2pTargetInterface();
- IP2pInitiator getP2pInitiatorInterface();
-
- void cancel();
- int createLlcpConnectionlessSocket(int sap);
- int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength);
- int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength);
- int deselectSecureElement();
- boolean disable();
- boolean enable();
- int getOpenTimeout();
- String getProperties(String param);
- int[] getSecureElementList();
- int getSelectedSecureElement();
- boolean isEnabled();
- int openP2pConnection();
- int openTagConnection();
- int selectSecureElement(int seId);
- void setOpenTimeout(int timeout);
- int setProperties(String param, String value);
-
-}
-
diff --git a/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl b/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl
deleted file mode 100644
index 8dcdf18..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/IP2pTarget.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.trustedlogic.trustednfc.android;
-
-/**
- * TODO
- *
- * {@hide}
- */
-interface IP2pTarget
-{
-
- byte[] getGeneralBytes(int nativeHandle);
- int getMode(int nativeHandle);
- int connect(int nativeHandle);
- boolean disconnect(int nativeHandle);
- byte[] transceive(int nativeHandle, in byte[] data);
-
-}
\ No newline at end of file
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java
index 0270626..eccdeb13 100644
--- a/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpConnectionlessSocket.java
@@ -24,7 +24,9 @@
import java.io.IOException;
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+import android.nfc.ErrorCodes;
+import android.nfc.ILlcpConnectionlessSocket;
+import android.nfc.LlcpPacket;
import android.os.RemoteException;
import android.util.Log;
@@ -32,19 +34,19 @@
/**
* LlcpConnectionlessSocket represents a LLCP Connectionless object to be used
* in a connectionless communication
- *
+ *
* @since AA02.01
* @hide
*/
public class LlcpConnectionlessSocket {
-
-
+
+
private static final String TAG = "LlcpConnectionlessSocket";
/**
* The handle returned by the NFC service and used to identify the LLCP connectionless socket in
* every call of this class.
- *
+ *
* @hide
*/
protected int mHandle;
@@ -52,15 +54,15 @@
/**
* The entry point for LLCP Connectionless socket operations.
- *
+ *
* @hide
*/
protected ILlcpConnectionlessSocket mService;
-
-
+
+
/**
* Internal constructor for the LlcpConnectionlessSocket class.
- *
+ *
* @param service The entry point to the Nfc Service for LLCP Connectionless socket class.
* @param handle The handle returned by the NFC service and used to identify
* the socket in subsequent calls.
@@ -73,7 +75,7 @@
/**
* Send data to a specific LLCP Connectionless client
- *
+ *
* @param packet Service Access Point number related to a LLCP
* Connectionless client and a data buffer to send
* @throws IOException if the LLCP link has been lost or deactivated.
@@ -93,7 +95,7 @@
/**
* Receive data from a LLCP Connectionless client
- *
+ *
* @return data data received from a specific LLCP Connectionless client
* @throws IOException if the LLCP link has been lost or deactivated.
* @see LlcpPacket
@@ -106,7 +108,7 @@
return packet;
}else{
// Handle potential errors
- throw new IOException();
+ throw new IOException();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in receiveFrom(): ", e);
@@ -116,7 +118,7 @@
/**
* Close the created Connectionless socket.
- *
+ *
* @since AA02.01
*/
public void close() {
@@ -129,13 +131,13 @@
/**
* Returns the local Service Access Point number of the socket
- *
+ *
* @return sap
* @since AA02.01
*/
public int getSap() {
int sap = 0;
-
+
try {
sap = mService.getSap(mHandle);
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java
index a152ecb1..1bdf72f 100644
--- a/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpServiceSocket.java
@@ -24,7 +24,9 @@
import java.io.IOException;
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+import android.nfc.ErrorCodes;
+import android.nfc.ILlcpSocket;
+import android.nfc.ILlcpServiceSocket;
import android.os.RemoteException;
import android.util.Log;
@@ -32,7 +34,7 @@
/**
* LlcpServiceSocket represents a LLCP Service to be used in a
* Connection-oriented communication
- *
+ *
* @since AA02.01
* @hide
*/
@@ -43,19 +45,19 @@
/**
* The handle returned by the NFC service and used to identify the LLCP
* Service socket in every call of this class.
- *
+ *
* @hide
*/
protected int mHandle;
/**
* The entry point for LLCP Service socket operations.
- *
+ *
* @hide
*/
protected ILlcpServiceSocket mService;
-
- private ILlcpSocket mLlcpSocketService;
+
+ private final ILlcpSocket mLlcpSocketService;
static LlcpException convertErrorToLlcpException(int errorCode) {
return convertErrorToLlcpException(errorCode, null);
@@ -83,7 +85,7 @@
/**
* Internal constructor for the LlcpServiceSocket class.
- *
+ *
* @param service
* The entry point to the Nfc Service for LlcpServiceSocket
* class.
@@ -101,14 +103,14 @@
/**
* Wait for incomming connection request from a LLCP client and accept this
* request
- *
+ *
* @return socket object to be used to communicate with a LLCP client
- *
+ *
* @throws IOException
* if the llcp link is lost or deactivated
* @throws LlcpException
* if not enough ressources are available
- *
+ *
* @see LlcpSocket
* @since AA02.01
*/
@@ -124,7 +126,7 @@
throw convertErrorToLlcpException(handle);
}
}
-
+
// Build the public LlcpSocket object
return new LlcpSocket(mLlcpSocketService, handle);
} catch (RemoteException e) {
@@ -136,7 +138,7 @@
/**
* Set the timeout for the accept request
- *
+ *
* @param timeout
* value of the timeout for the accept request
* @since AA02.01
@@ -151,7 +153,7 @@
/**
* Get the timeout value of the accept request
- *
+ *
* @return mTimeout
* @since AA02.01
*/
@@ -166,7 +168,7 @@
/**
* Close the created Llcp Service socket
- *
+ *
* @since AA02.01
*/
public void close() {
diff --git a/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java b/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java
index e47160c..ebde3e1 100644
--- a/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java
+++ b/core/java/com/trustedlogic/trustednfc/android/LlcpSocket.java
@@ -24,7 +24,8 @@
import java.io.IOException;
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
+import android.nfc.ErrorCodes;
+import android.nfc.ILlcpSocket;
import android.os.RemoteException;
import android.util.Log;
@@ -32,7 +33,7 @@
/**
* LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a
* connection-oriented communication
- *
+ *
* @since AA02.01
* @hide
*/
@@ -43,14 +44,14 @@
/**
* The handle returned by the NFC service and used to identify the LLCP
* socket in every call of this class.
- *
+ *
* @hide
*/
protected int mHandle;
/**
* The entry point for LLCP socket operations.
- *
+ *
* @hide
*/
protected ILlcpSocket mService;
@@ -84,7 +85,7 @@
/**
* Internal constructor for the LlcpSocket class.
- *
+ *
* @param service
* The entry point to the Nfc Service for LlcpServiceSocket
* class.
@@ -100,7 +101,7 @@
/**
* Connect request to a specific LLCP Service by its SAP.
- *
+ *
* @param sap
* Service Access Point number of the LLCP Service
* @throws IOException
@@ -128,7 +129,7 @@
/**
* Connect request to a specific LLCP Service by its Service Name.
- *
+ *
* @param sn
* Service Name of the LLCP Service
* @throws IOException
@@ -156,7 +157,7 @@
/**
* Set the timeout for the connect request
- *
+ *
* @param timeout
* timeout value for the connect request
* @since AA02.01
@@ -171,7 +172,7 @@
/**
* Get the timeout value of the connect request
- *
+ *
* @return mTimeout
* @since AA02.01
*/
@@ -187,7 +188,7 @@
/**
* Disconnect request to the connected LLCP socket and close the created
* socket.
- *
+ *
* @throws IOException
* if the LLCP has been lost or deactivated.
* @since AA02.01
@@ -206,7 +207,7 @@
/**
* Send data to the connected LLCP Socket.
- *
+ *
* @throws IOException
* if the LLCP has been lost or deactivated.
* @since AA02.01
@@ -220,12 +221,12 @@
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in send(): ", e);
- }
+ }
}
/**
* Receive data from the connected LLCP socket
- *
+ *
* @param receiveBuffer
* a buffer for the received data
* @return length length of the data received
@@ -242,14 +243,14 @@
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in send(): ", e);
- }
-
+ }
+
return receivedLength;
}
-
+
/**
* Returns the local Service Access Point number of the socket
- *
+ *
* @return localSap
* @since AA02.01
*/
@@ -264,7 +265,7 @@
/**
* Returns the local Maximum Information Unit(MIU) of the socket
- *
+ *
* @return miu
* @since AA02.01
*/
@@ -279,7 +280,7 @@
/**
* Returns the local Receive Window(RW) of the socket
- *
+ *
* @return rw
* @since AA02.01
*/
@@ -296,7 +297,7 @@
* Returns the remote Maximum Information Unit(MIU) of the socket.
* <p>
* This method must be called when the socket is in CONNECTED_STATE
- *
+ *
* @return remoteMiu
* @throws LlcpException
* if the LlcpClientSocket is not in a CONNECTED_STATE
@@ -320,7 +321,7 @@
* Returns the remote Receive Window(RW) of the connected remote socket.
* <p>
* This method must be called when the socket is in CONNECTED_STATE
- *
+ *
* @return rw
* @throws LlcpException
* if the LlcpClientSocket is not in a CONNECTED_STATE
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java b/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java
deleted file mode 100644
index f03b604..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/NdefMessage.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-/**
- * File : NDEFMessage.java
- * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
- * Created : 05-10-2009
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents an NDEF message as specified by the <a
- * href="http://www.nfc-forum.org/">NFC Forum</a>.
- *
- * @see NdefRecord
- *
- * @since AA01.04
- * @hide
- */
-public class NdefMessage implements Parcelable {
- /* Flag values */
- private static final int FLAG_MB = 0x80;
- private static final int FLAG_ME = 0x40;
- private static final int FLAG_CF = 0x20;
- private static final int FLAG_SR = 0x10;
- private static final int FLAG_IL = 0x08;
-
- /**
- * Array of {@link NdefRecord} composing this message.
- */
- private NdefRecord[] mRecords;
-
- /**
- * Builds an NDEF message.
- *
- * @param data raw NDEF message data
- *
- * @throws NFCException
- */
- public NdefMessage(byte[] data) throws NfcException {
- if (parseNdefMessage(data) == -1)
- throw new NfcException("Error while parsing NDEF message");
- }
-
- /**
- * Builds an NDEF message.
- *
- * @param records
- * an array of already created NDEF records
- */
- public NdefMessage(NdefRecord[] records) {
- mRecords = new NdefRecord[records.length];
-
- System.arraycopy(records, 0, mRecords, 0, records.length);
- }
-
- /**
- * Returns the NDEF message as a byte array.
- *
- * @return the message as a byte array
- */
- public byte[] toByteArray() {
- if ((mRecords == null) || (mRecords.length == 0))
- return null;
-
- byte[] msg = {};
-
- for (int i = 0; i < mRecords.length; i++) {
- byte[] record = mRecords[i].toByteArray();
- byte[] tmp = new byte[msg.length + record.length];
-
- /* Make sure the Message Begin flag is set only for the first record */
- if (i == 0)
- record[0] |= FLAG_MB;
- else
- record[0] &= ~FLAG_MB;
-
- /* Make sure the Message End flag is set only for the last record */
- if (i == (mRecords.length - 1))
- record[0] |= FLAG_ME;
- else
- record[0] &= ~FLAG_ME;
-
- System.arraycopy(msg, 0, tmp, 0, msg.length);
- System.arraycopy(record, 0, tmp, msg.length, record.length);
-
- msg = tmp;
- }
-
- return msg;
- }
-
- /**
- * Returns an array of {@link NdefRecord} composing this message.
- *
- * @return mRecords
- *
- * @since AA02.01
- */
- public NdefRecord[] getRecords(){
- return mRecords;
- }
-
- private native int parseNdefMessage(byte[] data);
-
- /**
- * (Parcelable) Describe the parcel
- * {@hide}
- */
- public int describeContents() {
- return 0;
- }
-
- /**
- * (Parcelable) Convert current object to a Parcel
- * {@hide}
- */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRecords.length);
- dest.writeTypedArray(mRecords, 0);
- }
-
- /**
- * Creator class, needed when implementing from Parcelable
- * {@hide}
- */
- public static final Parcelable.Creator<NdefMessage> CREATOR = new Parcelable.Creator<NdefMessage>() {
- public NdefMessage createFromParcel(Parcel in) {
- int recordsLength = in.readInt();
- NdefRecord[] records = new NdefRecord[recordsLength];
- in.readTypedArray(records, NdefRecord.CREATOR);
- return new NdefMessage(records);
- }
-
- public NdefMessage[] newArray(int size) {
- return new NdefMessage[size];
- }
- };
-
-}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java b/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java
deleted file mode 100644
index a0257fe..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/NdefRecord.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-/**
- * File : NdefRecord.java
- * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
- * Created : 05-10-2009
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import android.location.Location;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * An NDEF record as specified by the <a href="http://www.nfc-forum.org/">NFC
- * Forum</a>.
- *
- * @see NdefMessage
- *
- * @since AA01.04
- * @hide
- */
-public class NdefRecord implements Parcelable {
-
- /**
- * Type Name Format - Empty record
- */
- public static final short TNF_EMPTY = 0x0;
-
- /**
- * Type Name Format - NFC Forum-defined type
- */
- public static final short TNF_WELL_KNOWN_TYPE = 0x1;
-
- /**
- * Type Name Format - RFC2045 MIME type
- */
- public static final short TNF_MIME_MEDIA_TYPE = 0x2;
-
- /**
- * Type Name Format - Absolute URI
- */
- public static final short TNF_ABSOLUTE_URI = 0x3;
-
- /**
- * Type Name Format - User-defined type
- */
- public static final short TNF_EXTERNAL_TYPE = 0x4;
-
- /**
- * Type Name Format - Unknown type
- */
- public static final short TNF_UNKNOWN = 0x5;
-
- /**
- * Type Name Format - Unchanged. This TNF is used for chunked records, so
- * that middle records inherits from the first record's type.
- */
- public static final short TNF_UNCHANGED = 0x6;
-
- /**
- * NFC Forum-defined Type - Smart Poster
- */
- public static final byte[] TYPE_SMART_POSTER = { 0x53, 0x70 };
-
- /**
- * NFC Forum-defined Type - Text
- */
- public static final byte[] TYPE_TEXT = { 0x54 };
-
- /**
- * NFC Forum-defined Type - URI
- */
- public static final byte[] TYPE_URI = { 0x55 };
-
- /**
- * NFC Forum-defined Global Type - Connection Handover Request
- */
- public static final byte[] TYPE_HANDOVER_REQUEST = { 0x48, 0x72 };
-
- /**
- * NFC Forum-defined Global Type - Connection Handover Select
- */
- public static final byte[] TYPE_HANDOVER_SELECT = { 0x48, 0x73 };
-
- /**
- * NFC Forum-defined Global Type - Connection Handover Carrier
- */
- public static final byte[] TYPE_HANDOVER_CARRIER = { 0x48, 0x63 };
-
- /**
- * NFC Forum-defined Local Type - Alternative Carrier
- */
- public static final byte[] TYPE_ALTERNATIVE_CARRIER = { 0x61, 0x63 };
-
- /* Flag values */
- private static final int FLAG_MB = 0x80;
- private static final int FLAG_ME = 0x40;
- private static final int FLAG_CF = 0x20;
- private static final int FLAG_SR = 0x10;
- private static final int FLAG_IL = 0x08;
-
- /**
- * Record Flags
- */
- private short mFlags = 0;
-
- /**
- * Record Type Name Format
- */
- private short mTnf = 0;
-
- /**
- * Record Type
- */
- private byte[] mType = null;
-
- /**
- * Record Identifier
- */
- private byte[] mId = null;
-
- /**
- * Record Payload
- */
- private byte[] mPayload = null;
-
- /**
- * Creates an NdefRecord given its Type Name Format, its type, its id and
- * its.
- *
- * @param tnf
- * Type Name Format
- * @param type
- * record type
- * @param id
- * record id (optional, can be null)
- * @param data
- * record payload
- */
- public NdefRecord(short tnf, byte[] type, byte[] id, byte[] data) {
-
- /* generate flag */
- mFlags = FLAG_MB | FLAG_ME;
-
- /* Determine if it is a short record */
- if(data.length < 0xFF)
- {
- mFlags |= FLAG_SR;
- }
-
- /* Determine if an id is present */
- if(id.length != 0)
- {
- mFlags |= FLAG_IL;
- }
-
- mTnf = tnf;
- mType = (byte[]) type.clone();
- mId = (byte[]) id.clone();
- mPayload = (byte[]) data.clone();
- }
-
- /**
- * Appends data to the record's payload.
- *
- * @param data
- * Data to be added to the record.
- */
- public void appendPayload(byte[] data) {
- byte[] newPayload = new byte[mPayload.length + data.length];
-
- System.arraycopy(mPayload, 0, newPayload, 0, mPayload.length);
- System.arraycopy(data, 0, newPayload, mPayload.length, data.length);
-
- mPayload = newPayload;
- }
-
- /**
- * Returns record as a byte array.
- *
- * @return record as a byte array.
- */
- public byte[] toByteArray() {
- return generate(mFlags, mTnf, mType, mId, mPayload);
- }
-
- private native byte[] generate(short flags, short tnf, byte[] type,
- byte[] id, byte[] data);
-
- /**
- * (Parcelable) Describe the parcel
- * {@hide}
- */
- public int describeContents() {
- return 0;
- }
-
- /**
- * (Parcelable) Convert current object to a Parcel
- * {@hide}
- */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mTnf);
- dest.writeInt(mType.length);
- dest.writeByteArray(mType);
- dest.writeInt(mId.length);
- dest.writeByteArray(mId);
- dest.writeInt(mPayload.length);
- dest.writeByteArray(mPayload);
- }
-
- /**
- * Creator class, needed when implementing from Parcelable
- * {@hide}
- */
- public static final Parcelable.Creator<NdefRecord> CREATOR = new Parcelable.Creator<NdefRecord>() {
- public NdefRecord createFromParcel(Parcel in) {
- // TNF
- short tnf = (short)in.readInt();
- // Type
- int typeLength = in.readInt();
- byte[] type = new byte[typeLength];
- in.readByteArray(type);
- // ID
- int idLength = in.readInt();
- byte[] id = new byte[idLength];
- in.readByteArray(id);
- // Payload
- int payloadLength = in.readInt();
- byte[] payload = new byte[payloadLength];
- in.readByteArray(payload);
-
- return new NdefRecord(tnf, type, id, payload);
- }
-
- public NdefRecord[] newArray(int size) {
- return new NdefRecord[size];
- }
- };
-
- /**
- * Returns record TNF
- *
- * @return mTnf
- */
- public int getTnf(){
- return mTnf;
- }
-
- /**
- * Returns record TYPE
- *
- * @return mType
- */
- public byte[] getType(){
- return mType;
- }
-
- /**
- * Returns record ID
- *
- * @return mId
- */
- public byte[] getId(){
- return mId;
- }
-
- /**
- * Returns record Payload
- *
- * @return mPayload
- */
- public byte[] getPayload(){
- return mPayload;
- }
-
-}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NdefTag.java b/core/java/com/trustedlogic/trustednfc/android/NdefTag.java
deleted file mode 100644
index 1d99241..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/NdefTag.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-/**
- * File : NDEFTag.java
- * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
- * Created : 04-12-2009
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import java.io.IOException;
-
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * NdefTag represents tags complying with the NFC Forum's NFC Data Exchange
- * Format.
- *
- * @since AA01.04
- * @hide
- */
-public class NdefTag extends NfcTag {
-
- private static final String TAG = "NdefTag";
-
-
- public NdefTag(NfcTag tag){
- super(tag.mService,tag.mHandle);
- this.isConnected = tag.isConnected;
- this.isClosed = tag.isClosed;
- tag.isClosed = false;
- }
-
- /**
- * Internal constructor for the NfcNdefTag class.
- *
- * @param service The entry point to the Nfc Service for NfcNdefTag class.
- * @param handle The handle returned by the NFC service and used to identify
- * the tag in subsequent calls.
- * @hide
- */
- NdefTag(INfcTag service, int handle) {
- super(service, handle);
- }
-
- /**
- * Read NDEF data from an NDEF tag.
- *
- * @return the NDEF message read from the tag.
- * @throws NfcException if the tag is not NDEF-formatted.
- * @throws IOException if the target has been lost or the connection has
- * been closed.
- * @see NdefMessage
- */
- public NdefMessage read() throws NfcException, IOException {
- // Check state
- checkState();
-
- //Check if the tag is Ndef compliant
- if(isNdef != true){
- isNdef = isNdef();
- if(isNdef != true) {
- throw new NfcException("Tag is not NDEF compliant");
- }
- }
-
- // Perform transceive
- try {
- NdefMessage msg = mService.read(mHandle);
- if (msg == null) {
- throw new IOException("NDEF read failed");
- }
- return msg;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in read(): ", e);
- return null;
- }
- }
-
- /**
- * Write NDEF data to an NDEF-compliant tag.
- *
- * @param msg NDEF message to be written to the tag.
- * @throws NfcException if the tag is not NDEF formatted.
- * @throws IOException if the target has been lost or the connection has
- * been closed.
- * @see NdefMessage
- */
- public void write(NdefMessage msg) throws NfcException, IOException {
- // Check state
- checkState();
-
- //Check if the tag is Ndef compliant
- if(isNdef != true){
- isNdef = isNdef();
- if(isNdef != true) {
- throw new NfcException("Tag is not NDEF compliant");
- }
- }
-
- // Perform transceive
- try {
- boolean isSuccess = mService.write(mHandle, msg);
- if (!isSuccess) {
- throw new IOException("NDEF write failed");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in write(): ", e);
- }
- }
-}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcManager.java b/core/java/com/trustedlogic/trustednfc/android/NfcManager.java
deleted file mode 100644
index 98ab5bf..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/NfcManager.java
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-/**
- * File : NfcManager.java
- * Original-Author : Trusted Logic S.A. (Jeremie Corbier)
- * Created : 26-08-2009
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import java.io.IOException;
-
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.media.MiniThumbFile;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-
-//import android.util.Log;
-
-/**
- * This class provides the primary API for managing all aspects of NFC. Get an
- * instance of this class by calling
- * Context.getSystemService(Context.NFC_SERVICE).
- * @hide
- */
-public final class NfcManager {
- /**
- * Tag Reader Discovery mode
- */
- private static final int DISCOVERY_MODE_TAG_READER = 0;
-
- /**
- * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an
- * NFC-IP1 communication. Implementations should not assume that the
- * controller will end up behaving as an NFC-IP1 target or initiator and
- * should handle both cases, depending on the type of the remote peer type.
- */
- private static final int DISCOVERY_MODE_NFCIP1 = 1;
-
- /**
- * Card Emulation mode Enables the manager to act as an NFC tag. Provided
- * that a Secure Element (an UICC for instance) is connected to the NFC
- * controller through its SWP interface, it can be exposed to the outside
- * NFC world and be addressed by external readers the same way they would
- * with a tag.
- * <p>
- * Which Secure Element is exposed is implementation-dependent.
- *
- * @since AA01.04
- */
- private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
-
- /**
- * Used as Parcelable extra field in
- * {@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_TAG_DISCOVERED_ACTION}
- * . It contains the NDEF message read from the NDEF tag discovered.
- */
- public static final String NDEF_MESSAGE_EXTRA = "com.trustedlogic.trustednfc.android.extra.NDEF_MESSAGE";
-
- /**
- * Broadcast Action: a NDEF tag has been discovered.
- * <p>
- * Always contains the extra field
- * {@link com.trustedlogic.trustednfc.android.NfcManager#NDEF_MESSAGE_EXTRA}.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_NOTIFY permission.
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String NDEF_TAG_DISCOVERED_ACTION = "com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED";
-
- /**
- * Used as byte array extra field in
- * {@link com.trustedlogic.trustednfc.android.NfcManager#TRANSACTION_DETECTED_ACTION}
- * . It contains the AID of the applet concerned by the transaction.
- */
- public static final String AID_EXTRA = "com.trustedlogic.trustednfc.android.extra.AID";
-
- /**
- * Broadcast Action: a transaction with a secure element has been detected.
- * <p>
- * Always contains the extra field
- * {@link com.trustedlogic.trustednfc.android.NfcManager#AID_EXTRA}
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_NOTIFY permission
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String TRANSACTION_DETECTED_ACTION = "com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED";
-
- /**
- * LLCP link status: The LLCP link is activated.
- *
- * @since AA02.01
- */
- public static final int LLCP_LINK_STATE_ACTIVATED = 0;
-
- /**
- * LLCP link status: The LLCP link is deactivated.
- *
- * @since AA02.01
- */
- public static final int LLCP_LINK_STATE_DEACTIVATED = 1;
-
- /**
- * Used as int extra field in
- * {@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_ACTION}
- * . It contains the new state of the LLCP link.
- */
- public static final String LLCP_LINK_STATE_CHANGED_EXTRA = "com.trustedlogic.trustednfc.android.extra.LLCP_LINK_STATE";
-
- /**
- * Broadcast Action: the LLCP link state changed.
- * <p>
- * Always contains the extra field
- * {@link com.trustedlogic.trustednfc.android.NfcManager#LLCP_LINK_STATE_CHANGED_EXTRA}.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_LLCP permission.
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String LLCP_LINK_STATE_CHANGED_ACTION = "com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED";
-
- private static final String TAG = "NfcManager";
-
- private Handler mHandler;
-
- private INfcManager mService;
-
- private INfcTag mNfcTagService;
-
- private IP2pTarget mP2pTargetService;
-
- private IP2pInitiator mP2pInitiatorService;
-
- private ILlcpSocket mLlcpSocketService;
-
- private ILlcpConnectionlessSocket mLlcpConnectionlessSocketService;
-
- private ILlcpServiceSocket mLlcpServiceSocketService;
-
- static NfcException convertErrorToNfcException(int errorCode) {
- return convertErrorToNfcException(errorCode, null);
- }
-
- static NfcException convertErrorToNfcException(int errorCode, String message) {
- if (message == null) {
- message = "";
- } else {
- message = " (" + message + ")";
- }
-
- switch (errorCode) {
- case ErrorCodes.ERROR_BUSY:
- return new NfcException("Another operation is already pending" + message);
- case ErrorCodes.ERROR_CANCELLED:
- return new NfcException("Operation cancelled" + message);
- case ErrorCodes.ERROR_TIMEOUT:
- return new NfcException("Operation timed out" + message);
- case ErrorCodes.ERROR_SOCKET_CREATION:
- return new NfcException("Error during the creation of an Llcp socket:" + message);
- case ErrorCodes.ERROR_SAP_USED:
- return new NfcException("Error SAP already used:" + message);
- case ErrorCodes.ERROR_SERVICE_NAME_USED:
- return new NfcException("Error Service Name already used:" + message);
- case ErrorCodes.ERROR_SOCKET_OPTIONS:
- return new NfcException("Error Socket options:" + message);
- case ErrorCodes.ERROR_INVALID_PARAM:
- return new NfcException("Error Set Properties: invalid param" + message);
- case ErrorCodes.ERROR_NFC_ON:
- return new NfcException("Error Set Properties : NFC is ON" + message);
- case ErrorCodes.ERROR_NOT_INITIALIZED:
- return new NfcException("NFC is not running " + message);
- case ErrorCodes.ERROR_SE_ALREADY_SELECTED:
- return new NfcException("Secure Element already connected" + message);
- case ErrorCodes.ERROR_NO_SE_CONNECTED:
- return new NfcException("No Secure Element connected" + message);
- case ErrorCodes.ERROR_SE_CONNECTED:
- return new NfcException("A secure Element is already connected" + message);
- default:
- return new NfcException("Unkown error code " + errorCode + message);
- }
- }
-
- /**
- * @hide
- */
- public NfcManager(INfcManager service, Handler handler) {
- mService = service;
- mHandler = handler;
- try {
- mNfcTagService = mService.getNfcTagInterface();
- mP2pInitiatorService = mService.getP2pInitiatorInterface();
- mP2pTargetService = mService.getP2pTargetInterface();
- mLlcpServiceSocketService = mService.getLlcpServiceInterface();
- mLlcpConnectionlessSocketService = mService.getLlcpConnectionlessInterface();
- mLlcpSocketService = mService.getLlcpInterface();
- } catch (RemoteException e) {
- mLlcpSocketService = null;
- mNfcTagService = null;
- mP2pInitiatorService = null;
- mP2pTargetService = null;
- mLlcpConnectionlessSocketService = null;
- mLlcpServiceSocketService = null;
- }
- }
-
- /**
- * Return the status of the NFC feature
- *
- * @return mIsNfcEnabled
- * @since AA02.01
- */
- public boolean isEnabled() {
- try {
- return mService.isEnabled();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in isEnabled(): ", e);
- return false;
- }
- }
-
- /**
- * Enable the NFC Feature
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_ADMIN permission
- *
- * @throws NfcException if the enable failed
- * @since AA02.01
- */
- public void enable() throws NfcException {
- try {
- boolean isSuccess = mService.enable();
- if (isSuccess == false) {
- throw new NfcException("NFC Service failed to enable");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in enable(): ", e);
- }
- }
-
- /**
- * Disable the NFC feature
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_ADMIN permission
- *
- * @throws NfcException if the disable failed
- * @since AA02.01
- */
- public void disable() throws NfcException {
- try {
- boolean isSuccess = mService.disable();
- if (isSuccess == false) {
- throw new NfcException("NFC Service failed to disable");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in disable(): ", e);
- }
- }
-
- /**
- * Get the list of the identifiers of the Secure Elements detected
- * by the NFC controller.
- *
- * @return list a list of Secure Element identifiers.
- * @see #getSelectedSecureElement
- * @see #selectSecureElement(int)
- * @see #deselectSecureElement
- * @since AA02.01
- */
- public int[] getSecureElementList() {
- try {
- return mService.getSecureElementList();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getSecureElementList(): ", e);
- return null;
- }
- }
-
- /**
- * Get the identifier of the currently selected secure element.
- *
- * @return id identifier of the currently selected Secure Element. 0 if none.
- * @see #getSecureElementList
- * @see #selectSecureElement(int)
- * @see #deselectSecureElement
- * @since AA02.01
- */
- public int getSelectedSecureElement() {
- try {
- return mService.getSelectedSecureElement();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getSelectedSecureElement(): ", e);
- return -1;
- }
- }
-
- /**
- * Select a specific Secure Element by its identifier.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_ADMIN permission
- *
- * @throws NfcException if a or this secure element is already selected
- * @see #getSecureElementList
- * @see #getSelectedSecureElement
- * @see #deselectSecureElement
- * @since AA02.01
- */
- public void selectSecureElement(int seId) throws NfcException {
- try {
- int status = mService.selectSecureElement(seId);
- if(status != ErrorCodes.SUCCESS){
- throw convertErrorToNfcException(status);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in selectSecureElement(): ", e);
- }
- }
-
- /**
- * Deselect the currently selected Secure Element
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_ADMIN permission
- *
- * @throws NfcException if no secure Element is selected
- * @see #getSecureElementList
- * @see #getSelectedSecureElement
- * @see #selectSecureElement(int)
- * @since AA02.01
- */
- public void deselectSecureElement() throws NfcException {
- try {
- int status = mService.deselectSecureElement();
- if(status != ErrorCodes.SUCCESS){
- throw convertErrorToNfcException(status);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in deselectSecureElement(): ", e);
- }
- }
-
- /**
- * Open a connection with a remote NFC peer
- *
- * This method does not return while no remote NFC peer enters the field.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_RAW permission
- *
- * @return P2pDevice object to be used to communicate with the detected
- * peer.
- * @throws IOException if the target has been lost or the connection has
- * been closed.
- * @throws NfcException if an open is already started
- * @see P2pDevice
- * @see #getOpenTimeout
- * @see #setOpenTimeout(int)
- * @see #cancel
- * @since AA02.01
- */
- public P2pDevice openP2pConnection() throws IOException, NfcException {
- try {
- int handle = mService.openP2pConnection();
- // Handle potential errors
- if (ErrorCodes.isError(handle)) {
- if (handle == ErrorCodes.ERROR_IO) {
- throw new IOException();
- } else {
- throw convertErrorToNfcException(handle);
- }
- }
- // Build the public NfcTag object, depending on its type
- if (mP2pTargetService.getMode(handle) == P2pDevice.MODE_P2P_TARGET) {
- return new P2pTarget(mP2pTargetService, handle);
- } else {
- return new P2pInitiator(mP2pInitiatorService, handle);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in openTagConnection(): ", e);
- return null;
- }
- }
-
- /**
- * Open a connection with a tag
- *
- * This method does not return while no tag enters the field.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_RAW permission
- *
- * @return tag object to be use to communicate with the detected NfcTag.
- * @throws IOException if the target has been lost or the connection has
- * been closed.
- * @throws NfcException if an open is already started
- * @see NfcTag
- * @see #getOpenTimeout
- * @see #setOpenTimeout(int)
- * @see #cancel
- * @since AA02.01
- */
- public NfcTag openTagConnection() throws IOException, NfcException {
- try {
- int handle = mService.openTagConnection();
- // Handle potential errors
- if (ErrorCodes.isError(handle)) {
- if (handle == ErrorCodes.ERROR_IO) {
- throw new IOException();
- } else {
- throw convertErrorToNfcException(handle);
- }
- }
- // Build the public NfcTag object
- return new NfcTag(mNfcTagService, handle);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in openTagConnection(): ", e);
- return null;
- }
- }
-
- /**
- * Set the timeout for open requests
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_RAW permission
- *
- * @param timeout value of the timeout for open request
- * @see #openP2pConnection
- * @see #openTagConnection
- * @see #getOpenTimeout
- * @since AA02.01
- */
- public void setOpenTimeout(int timeout) {
- try {
- mService.setOpenTimeout(timeout);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in setOpenTimeout(): ", e);
- }
- }
-
- /**
- * Get the timeout value of open requests
- *
- * @return mTimeout
- * @see #setOpenTimeout(int)
- * @since AA02.01
- */
- public int getOpenTimeout() {
- try {
- return mService.getOpenTimeout();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getOpenTimeout(): ", e);
- return 0;
- }
- }
-
- /**
- * Cancel an openTagConnection or an openP2pConnection started
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_RAW permission
- *
- * @see #openP2pConnection
- * @see #openTagConnection
- * @since AA02.01
- */
- public void cancel() {
- try {
- mService.cancel();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in cancel(): ", e);
- }
- }
-
- /**
- * Creates a connectionless socket for a LLCP link and set its Service
- * Access Point number (SAP)
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_LLCP permission
- *
- * @param sap Service Access Point number related to the created
- * Connectionless socket.
- * @return LlcpConnectionlessSocket object to be used in a LLCP
- * Connectionless communication.
- * @throws IOException if the socket creation failed
- * @throws NfcException if socket ressources are insufficicent
- * @see LlcpConnectionlessSocket
- * @since AA02.01
- */
- public LlcpConnectionlessSocket createLlcpConnectionlessSocket(int sap) throws IOException,
- NfcException {
-
- try {
- int handle = mService.createLlcpConnectionlessSocket(sap);
- // Handle potential errors
- if (ErrorCodes.isError(handle)) {
- if (handle == ErrorCodes.ERROR_IO) {
- throw new IOException();
- } else {
- throw convertErrorToNfcException(handle);
- }
- }
-
- // Build the public LlcpConnectionLess object
- return new LlcpConnectionlessSocket(mLlcpConnectionlessSocketService, handle);
-
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in createLlcpConnectionlessSocket(): ", e);
- return null;
- }
- }
-
- /**
- * Creates a LlcpServiceSocket for a LLCP link, set its Service Access Point
- * number (SAP).
- * <p>
- * During a LLCP communication, the LlcpServiceSocket will create LlcpSocket
- * to communicate with incoming LLCP clients. For that, a server socket need
- * to have some informations as a working buffer length in order to handle
- * incoming data and some options to define the LLCP communication.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_LLCP permission
- *
- * @param sap
- * @param sn Service Name of the LlcpServiceSocket
- * @param miu Maximum Information Unit (MIU) for a LlcpSocket created by the
- * LlcpServiceSocket
- * @param rw Receive Window (RW) for a LlcpSocket created by the
- * LlcpServiceSocket
- * @param linearBufferLength size of the memory space needed to handle
- * incoming data for every LlcpSocket created.
- * @return LlcpServiceSocket object to be used as a LLCP Service in a
- * connection oriented communication.
- * @throws IOException if the socket creation failed
- * @throws NfcException if socket ressources are insufficicent
- * @see LlcpServiceSocket
- * @since AA02.01
- */
- public LlcpServiceSocket createLlcpServiceSocket(int sap, String sn, int miu, int rw,
- int linearBufferLength) throws IOException, NfcException {
- try {
- int handle = mService.createLlcpServiceSocket(sap, sn, miu, rw, linearBufferLength);
- // Handle potential errors
- if (ErrorCodes.isError(handle)) {
- if (handle == ErrorCodes.ERROR_IO) {
- throw new IOException();
- } else {
- throw convertErrorToNfcException(handle);
- }
- }
-
- // Build the public LlcpServiceSocket object
- return new LlcpServiceSocket(mLlcpServiceSocketService, mLlcpSocketService, handle);
-
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in createLlcpServiceSocket(): ", e);
- return null;
- }
- }
-
- /**
- * Creates a LlcpSocket for a LLCP link with a specific Service Access Point
- * number (SAP)
- * <p>
- * A LlcpSocket need to have a linear buffer in order to handle incoming
- * data. This linear buffer will be used to store incoming data as a stream.
- * Data will be readable later.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_LLCP permission
- *
- * @param sap Service Access Point number for the created socket
- * @param miu Maximum Information Unit (MIU) of the communication socket
- * @param rw Receive Window (RW) of the communication socket
- * @param linearBufferLength size of the memory space needed to handle
- * incoming data with this socket
- * @throws IOException if the socket creation failed
- * @throws NfcException if socket ressources are insufficicent
- * @see LlcpSocket
- * @since AA02.01
- */
- public LlcpSocket createLlcpSocket(int sap, int miu, int rw, int linearBufferLength)
- throws IOException, NfcException {
- try {
- int handle = mService.createLlcpSocket(sap, miu, rw, linearBufferLength);
- // Handle potential errors
- if (ErrorCodes.isError(handle)) {
- if (handle == ErrorCodes.ERROR_IO) {
- throw new IOException();
- } else {
- throw convertErrorToNfcException(handle);
- }
- }
- // Build the public LlcpSocket object
- return new LlcpSocket(mLlcpSocketService, handle);
-
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in createLlcpSocket(): ", e);
- return null;
- }
- }
-
- /**
- * Set different parameters like the NCIP General bytes, the LLCP link
- * parameters and all tag discovery parameters.
- * <p class="note">
- * <strong>Note:</strong> Requires the NFC_ADMIN permission
- *
- * @param param parameter to be updated with a new value
- * @param value new value of the parameter
- * @throws NfcException if incorrect parameters of NFC is ON
- * @since AA02.01
- */
- public void setProperties(String param, String value) throws NfcException {
- try {
- int result = mService.setProperties(param, value);
- // Handle potential errors
- if (ErrorCodes.isError(result)) {
- throw convertErrorToNfcException(result);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in setProperties(): ", e);
- }
- }
-
- /**
- * Get the value of different parameters like the NCFIP General bytes, the
- * LLCP link parameters and all tag discovery parameters.
- *
- * @param param parameter to be updated
- * @return String value of the requested parameter
- * @throws RemoteException
- * @since AA02.01
- */
- public String getProperties(String param) {
- String value;
- try {
- value = mService.getProperties(param);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getProperties(): ", e);
- return null;
- }
- return value;
- }
-
-}
diff --git a/core/java/com/trustedlogic/trustednfc/android/NfcTag.java b/core/java/com/trustedlogic/trustednfc/android/NfcTag.java
deleted file mode 100644
index 798c7e4..0000000
--- a/core/java/com/trustedlogic/trustednfc/android/NfcTag.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-/**
- * File : NFCTag.java
- * Original-Author : Trusted Logic S.A. (Daniel Tomas)
- * Created : 26-02-2010
- */
-
-package com.trustedlogic.trustednfc.android;
-
-import java.io.IOException;
-
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
-
-/**
- * This class represents tags with no known formatting. One can use the method
- * {@link #isNdef()} to determine if the tag can store NDEF-formatted messages.
- * <p>
- *
- * <pre class="prettyprint">
- * if (tag.isNdef()) {
- * NdefTag ndefTag = (NdefTag) tag;
- * NdefMessage msg = ndefTag.read();
- * }
- * </pre>
- *
- * @since AA01.04
- * @see NdefMessage
- * @hide
- */
-public class NfcTag {
-
- private static final String TAG = "NfcTag";
-
- /**
- * The handle returned by the NFC service and used to identify the tag in
- * every call of this class.
- *
- * @hide
- */
- protected int mHandle;
-
- /**
- * The entry point for tag operations.
- *
- * @hide
- */
- protected INfcTag mService;
-
- /**
- * Flag set when the object is closed and thus not usable any more.
- *
- * @hide
- */
- protected boolean isClosed = false;
-
- /**
- * Flag set when the tag is connected.
- *
- * @hide
- */
- protected boolean isConnected = false;
-
- /**
- * Flag set when a check NDEF is performed.
- *
- * @hide
- */
- protected boolean isNdef = false;
-
- /**
- * Check if tag is still opened.
- *
- * @return data sent by the P2pInitiator.
- * @throws NfcException if accessing a closed target.
- *
- * @hide
- */
- public void checkState() throws NfcException {
- if (isClosed) {
- throw new NfcException("Tag has been closed.");
- }
- if (!isConnected) {
- throw new NfcException("Tag is not connected.");
- }
- }
-
- /**
- * Internal constructor for the NfcTag class.
- *
- * @param service The entry point to the Nfc Service for NfcTag class.
- * @param handle The handle returned by the NFC service and used to identify
- * the tag in subsequent calls.
- * @hide
- */
- NfcTag(INfcTag service, int handle) {
- this.mService = service;
- this.mHandle = handle;
- }
-
- /**
- * Connects to the tag. This shall be called prior to any other operation on
- * the tag.
- *
- * @throws IOException if the tag has been lost or the connection has been
- * closed.
- * @throws nfcException if the tag is already in connected state.
- */
- public void connect() throws NfcException, IOException {
- // Check state
- if (isClosed) {
- throw new NfcException("Tag has been closed.");
- }
- if (isConnected) {
- throw new NfcException("Already connected");
- }
-
- // Perform connect
- try {
- int result = mService.connect(mHandle);
- if (ErrorCodes.isError(result)) {
- if (result == ErrorCodes.ERROR_IO) {
- throw new IOException("Failed to connect");
- }
- else {
- throw NfcManager.convertErrorToNfcException(result);
- }
- }
- isConnected = true;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in connect(): ", e);
- }
- }
-
- /**
- * Disconnects from the tag. This must be called so that other targets can
- * be discovered. It restarts the NFC discovery loop.
- *
- * @throws NfcException if the tag is already in disconnected state or not connected
- */
- public void close() throws NfcException {
- // Check state
- checkState();
-
- try {
- mService.close(mHandle);
- isClosed = true;
- isConnected = false;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in close(): ", e);
- }
- }
-
- /**
- * Exchanges raw data with the tag, whatever the tag type.
- *
- * To exchange APDUs with a ISO14443-4-compliant tag, the data parameter
- * must be filled with the C-APDU (CLA, INS, P1, P2 [, ...]). The returned
- * data consists of the R-APDU ([...,] SW1, SW2).
- *
- * @param data data to be sent to the tag
- * @return data sent in response by the tag
- * @throws IOException if the tag has been lost or the connection has been
- * closed.
- * @throws NfcException in case of failure within the stack
- */
- public byte[] transceive(byte[] data) throws IOException, NfcException {
- // Check state
- checkState();
-
- // Perform transceive
- try {
- byte[] response = mService.transceive(mHandle, data);
- if (response == null) {
- throw new IOException("Transceive failed");
- }
- return response;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in transceive(): ", e);
- return null;
- }
- }
-
- /**
- * Checks whether tag is NDEF-compliant or not.
- *
- * @return true if the tag is NDEF-compliant, false otherwise
- * @throws NfcException in case an error occurred when trying to determine
- * whether the tag is NDEF-compliant
- */
- public boolean isNdef() throws NfcException {
- // Check state
- checkState();
-
- // Perform Check Ndef
- try {
- isNdef = mService.isNdef(mHandle);
- return isNdef;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in isNdef(): ", e);
- return false;
- }
- }
-
- /**
- * Returns target type. constants.
- *
- * @return tag type.
- */
- public String getType() {
- try {
- return mService.getType(mHandle);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getType(): ", e);
- return null;
- }
- }
-
- /**
- * Returns target UID.
- *
- * @return tag UID.
- */
- public byte[] getUid() {
- try {
- return mService.getUid(mHandle);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getType(): ", e);
- return null;
- }
- }
-
-}
diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java b/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java
index 0f28ae0..6b93bce 100644
--- a/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java
+++ b/core/java/com/trustedlogic/trustednfc/android/P2pInitiator.java
@@ -23,15 +23,14 @@
import java.io.IOException;
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
-
+import android.nfc.IP2pInitiator;
import android.os.RemoteException;
import android.util.Log;
/**
* P2pInitiator represents the initiator in an NFC-IP1 peer-to-peer
* communication.
- *
+ *
* @see P2pTarget
* @since AA02.01
* @hide
@@ -44,24 +43,24 @@
* The entry point for P2P tag operations.
* @hide
*/
- private IP2pInitiator mService;
-
+ private final IP2pInitiator mService;
+
/**
* Internal constructor for the P2pInitiator class.
- *
+ *
* @param handle The handle returned by the NFC service and used to identify
* the tag in subsequent calls.
- *
+ *
* @hide
*/
P2pInitiator(IP2pInitiator service, int handle) {
this.mService = service;
this.mHandle = handle;
- }
+ }
/**
* Receives data from a P2pInitiator.
- *
+ *
* @return data sent by the P2pInitiator.
* @throws IOException if the target has been lost or if the connection has
* been closed.
@@ -81,7 +80,7 @@
/**
* Sends data to a P2pInitiator.
- *
+ *
* @param data data to be sent to the P2pInitiator.
* @throws IOException if the target has been lost or if the connection has
* been closed.
diff --git a/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java b/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java
index b5e00db..aa9e94f 100644
--- a/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java
+++ b/core/java/com/trustedlogic/trustednfc/android/P2pTarget.java
@@ -23,14 +23,14 @@
import java.io.IOException;
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
-
+import android.nfc.ErrorCodes;
+import android.nfc.IP2pTarget;
import android.os.RemoteException;
import android.util.Log;
/**
* P2pTarget represents the target in an NFC-IP1 peer-to-peer communication.
- *
+ *
* @see P2pInitiator
* @since AA02.01
* @hide
@@ -43,27 +43,27 @@
* The entry point for P2P tag operations.
* @hide
*/
- private IP2pTarget mService;
-
+ private final IP2pTarget mService;
+
/**
* Flag set when the object is closed and thus not usable any more.
* @hide
*/
- private boolean isClosed = false;
-
+ private final boolean isClosed = false;
+
/**
* Flag set when the tag is connected.
* @hide
*/
private boolean isConnected = false;
-
+
/**
* Check if tag is still opened.
- *
+ *
* @return data sent by the P2pInitiator.
* @throws NfcException if accessing a closed target.
- *
- * @hide
+ *
+ * @hide
*/
public void checkState() throws NfcException {
if(isClosed) {
@@ -73,21 +73,21 @@
/**
* Internal constructor for the P2pTarget class.
- *
+ *
* @param handle The handle returned by the NFC service and used to identify
* the tag in subsequent calls.
- *
+ *
* @hide
*/
P2pTarget(IP2pTarget service, int handle) {
this.mService = service;
this.mHandle = handle;
- }
+ }
/**
* Connects to the P2pTarget. This shall be called prior to any other
* operation on the P2pTarget.
- *
+ *
* @throws NfcException
*/
public void connect() throws NfcException {
@@ -96,7 +96,7 @@
if (isConnected) {
throw new NfcException("Already connected");
}
-
+
// Perform connect
try {
int result = mService.connect(mHandle);
@@ -105,7 +105,8 @@
throw new NfcException("Failed to connect");
}
else {
- throw NfcManager.convertErrorToNfcException(result);
+ // TODO(nxp)
+ // throw NfcAdapter.convertErrorToNfcException(result);
}
}
isConnected = true;
@@ -117,7 +118,7 @@
/**
* Disconnects from the P2p Target. This must be called so that other
* targets can be discovered. It restarts the NFC discovery loop.
- *
+ *
* @throws NFCException
*/
public void disconnect() throws NfcException {
@@ -132,7 +133,7 @@
/**
* Exchanges raw data with the P2pTarget.
- *
+ *
* @param data data to be sent to the P2pTarget
* @return data sent in response by the P2pTarget
* @throws IOException if the target has been lost or the connection has
@@ -158,10 +159,11 @@
/**
* Get the General bytes of the connected P2P Target
- *
+ *
* @return general bytes of the connected P2P Target
* @throws IOException if the target in not in connected state
*/
+ @Override
public byte[] getGeneralBytes() throws IOException {
try {
if(isConnected){
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java
index ccfbeb4..eff01b6 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpConnectionlessSocket.java
@@ -22,47 +22,47 @@
package com.trustedlogic.trustednfc.android.internal;
-import com.trustedlogic.trustednfc.android.LlcpPacket;
+import android.nfc.LlcpPacket;
/**
* LlcpConnectionlessSocket represents a LLCP Connectionless object to be used
* in a connectionless communication
- *
+ *
* @since AA02.01
- * {@hide}
+ * @hide
*/
public class NativeLlcpConnectionlessSocket {
-
- private int mHandle;
-
- private int mSap;
-
- private int mLinkMiu;
-
- public NativeLlcpConnectionlessSocket(){;
- }
-
- public NativeLlcpConnectionlessSocket(int sap){
- mSap = sap;
- }
-
+
+ private int mHandle;
+
+ private int mSap;
+
+ private int mLinkMiu;
+
+ public NativeLlcpConnectionlessSocket(){;
+ }
+
+ public NativeLlcpConnectionlessSocket(int sap){
+ mSap = sap;
+ }
+
public native boolean doSendTo(int sap, byte[] data);
public native LlcpPacket doReceiveFrom(int linkMiu);
public native boolean doClose();
-
+
public int getLinkMiu(){
- return mLinkMiu;
+ return mLinkMiu;
}
-
+
public int getSap(){
- return mSap;
+ return mSap;
}
-
+
public int getHandle(){
- return mHandle;
+ return mHandle;
}
}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java
index a01f135..079d69b 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpServiceSocket.java
@@ -30,53 +30,53 @@
public class NativeLlcpServiceSocket {
- private int mHandle;
-
- private int mLocalMiu;
-
- private int mLocalRw;
-
- private int mLocalLinearBufferLength;
-
- private int mSap;
-
- private int mTimeout;
-
- private String mServiceName;
-
- public NativeLlcpServiceSocket(){
-
- }
-
- public NativeLlcpServiceSocket(String serviceName){
- mServiceName = serviceName;
- }
-
+ private int mHandle;
+
+ private int mLocalMiu;
+
+ private int mLocalRw;
+
+ private int mLocalLinearBufferLength;
+
+ private int mSap;
+
+ private int mTimeout;
+
+ private String mServiceName;
+
+ public NativeLlcpServiceSocket(){
+
+ }
+
+ public NativeLlcpServiceSocket(String serviceName){
+ mServiceName = serviceName;
+ }
+
public native NativeLlcpSocket doAccept(int timeout, int miu, int rw, int linearBufferLength);
public native boolean doClose();
-
+
public int getHandle(){
- return mHandle;
+ return mHandle;
}
-
+
public void setAcceptTimeout(int timeout){
- mTimeout = timeout;
+ mTimeout = timeout;
}
-
+
public int getAcceptTimeout(){
- return mTimeout;
+ return mTimeout;
}
-
+
public int getRw(){
- return mLocalRw;
+ return mLocalRw;
}
-
+
public int getMiu(){
- return mLocalMiu;
+ return mLocalMiu;
}
-
+
public int getLinearBufferLength(){
- return mLocalLinearBufferLength;
+ return mLocalLinearBufferLength;
}
}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java
index 077c5e0..818cfaa 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeLlcpSocket.java
@@ -25,29 +25,29 @@
/**
* LlcpClientSocket represents a LLCP Connection-Oriented client to be used in a
* connection-oriented communication
- * {@hide}
+ * @hide
*/
public class NativeLlcpSocket {
-
- private int mHandle;
-
- private int mSap;
-
- private int mLocalMiu;
-
- private int mLocalRw;
-
+
+ private int mHandle;
+
+ private int mSap;
+
+ private int mLocalMiu;
+
+ private int mLocalRw;
+
private int mTimeout;
-
+
public NativeLlcpSocket(){
-
+
}
-
+
public NativeLlcpSocket(int sap, int miu, int rw){
- mSap = sap;
- mLocalMiu = miu;
- mLocalRw = rw;
+ mSap = sap;
+ mLocalMiu = miu;
+ mLocalRw = rw;
}
public native boolean doConnect(int nSap, int timeout);
@@ -59,35 +59,35 @@
public native boolean doSend(byte[] data);
public native int doReceive(byte[] recvBuff);
-
+
public native int doGetRemoteSocketMiu();
-
+
public native int doGetRemoteSocketRw();
-
-
-
+
+
+
public void setConnectTimeout(int timeout){
- mTimeout = timeout;
+ mTimeout = timeout;
}
-
+
public int getConnectTimeout(){
- return mTimeout;
+ return mTimeout;
}
-
+
public int getSap(){
- return mSap;
+ return mSap;
}
-
+
public int getMiu(){
- return mLocalMiu;
+ return mLocalMiu;
}
-
+
public int getRw(){
- return mLocalRw;
+ return mLocalRw;
}
-
+
public int getHandle(){
- return mHandle;
+ return mHandle;
}
}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java
index d1e64a6..819b0395 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNdefTag.java
@@ -25,7 +25,7 @@
/**
* Native interface to the NDEF tag functions
*
- * {@hide}
+ * @hide
*/
public class NativeNdefTag {
private int mHandle;
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
index 2f5a0f09..4632794 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcManager.java
@@ -24,36 +24,40 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import com.trustedlogic.trustednfc.android.NfcManager;
-import com.trustedlogic.trustednfc.android.NdefMessage;
-import com.trustedlogic.trustednfc.android.NfcTag;
+import android.nfc.FormatException;
+import android.nfc.NdefTag;
+import android.nfc.NfcAdapter;
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
/**
- * Native interface to the NFC Manager functions {@hide}
+ * Native interface to the NFC Manager functions
+ * @hide
*/
public class NativeNfcManager {
-
+
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA = "com.trustedlogic.trustednfc.android.extra.INTERNAL_LLCP_LINK_STATE";
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION = "com.trustedlogic.trustednfc.android.action.INTERNAL_LLCP_LINK_STATE_CHANGED";
-
+
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String INTERNAL_TARGET_DESELECTED_ACTION = "com.trustedlogic.trustednfc.android.action.INTERNAL_TARGET_DESELECTED";
/* Native structure */
private int mNative;
- private Context mContext;
+ private final Context mContext;
- private Handler mNfcHandler;
+ private final Handler mNfcHandler;
private static final String TAG = "NativeNfcManager";
@@ -95,7 +99,7 @@
/**
* Disables an NFCManager mode of operation. Allows to disable tag reader,
* peer to peer initiator or target modes.
- *
+ *
* @param mode discovery mode to enable. Must be one of the provided
* NFCManager.DISCOVERY_MODE_* constants.
*/
@@ -130,45 +134,81 @@
public native boolean doActivateLlcp();
private class NfcHandler extends Handler {
+
+ private int convertType(String typeName) {
+ if (typeName.equals("Iso14443")) {
+ return Tag.NFC_TAG_ISO14443_4B;
+ } else if (typeName.equals("MifareUL")) {
+ return Tag.NFC_TAG_MIFARE;
+ } else if (typeName.equals("Mifare1K")) {
+ return Tag.NFC_TAG_MIFARE;
+ } else if (typeName.equals("Mifare4K")) {
+ return Tag.NFC_TAG_MIFARE;
+ } else if (typeName.equals("MifareDESFIRE")) {
+ return Tag.NFC_TAG_MIFARE;
+ } else if (typeName.equals("Unknown Mifare")) {
+ return Tag.NFC_TAG_MIFARE;
+ } else if (typeName.equals("Felica")) {
+ return Tag.NFC_TAG_FELICA;
+ } else if (typeName.equals("Jewel")) {
+ return Tag.NFC_TAG_JEWEL;
+ } else {
+ return Tag.NFC_TAG_OTHER;
+ }
+ }
+
@Override
public void handleMessage(Message msg) {
try {
switch (msg.what) {
case MSG_NDEF_TAG:
- Log.d(TAG, "Checking for NDEF tag message");
- NativeNfcTag tag = (NativeNfcTag) msg.obj;
- if (tag.doConnect()) {
- if (tag.checkNDEF()) {
- byte[] buff = tag.doRead();
+ Log.d(TAG, "Tag detected, notifying applications");
+ NativeNfcTag nativeTag = (NativeNfcTag) msg.obj;
+ if (nativeTag.doConnect()) {
+ if (nativeTag.checkNDEF()) {
+ byte[] buff = nativeTag.doRead();
if (buff != null) {
- NdefMessage msgNdef = new NdefMessage(buff);
- if (msgNdef != null) {
- /* Send broadcast ordered */
- Intent NdefMessageIntent = new Intent();
- NdefMessageIntent
- .setAction(NfcManager.NDEF_TAG_DISCOVERED_ACTION);
- NdefMessageIntent.putExtra(NfcManager.NDEF_MESSAGE_EXTRA,
- msgNdef);
- Log.d(TAG, "NDEF message found, broadcasting to applications");
- mContext.sendOrderedBroadcast(NdefMessageIntent,
- android.Manifest.permission.NFC_NOTIFY);
- /* Disconnect tag */
- tag.doAsyncDisconnect();
+ NdefMessage[] msgNdef = new NdefMessage[1];
+ try {
+ msgNdef[0] = new NdefMessage(buff);
+ NdefTag tag = new NdefTag(convertType(nativeTag.getType()), nativeTag.getUid(), nativeTag.getHandle(), msgNdef);
+ Intent intent = new Intent();
+ intent.setAction(NfcAdapter.ACTION_NDEF_TAG_DISCOVERED);
+ intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Log.d(TAG, "NDEF tag found, starting corresponding activity");
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No activity found, disconnecting");
+ nativeTag.doAsyncDisconnect();
+ }
+ } catch (FormatException e) {
+ Log.w(TAG, "Unable to create NDEF message object (tag empty or not well formated)");
+ nativeTag.doAsyncDisconnect();
}
} else {
- Log.w(TAG, "Unable to read NDEF message (tag empty or not well formated)");
- /* Disconnect tag */
- tag.doAsyncDisconnect();
+ Log.w(TAG, "Unable to read NDEF message (tag empty or not well formated)");
+ nativeTag.doAsyncDisconnect();
}
} else {
- Log.d(TAG, "Tag is *not* NDEF compliant");
- /* Disconnect tag */
- tag.doAsyncDisconnect();
+ Intent intent = new Intent();
+ Tag tag = new Tag(convertType(nativeTag.getType()), false, nativeTag.getUid(), nativeTag.getHandle());
+ intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
+ intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Log.d(TAG, "Non-NDEF tag found, starting corresponding activity");
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No activity found, disconnecting");
+ nativeTag.doAsyncDisconnect();
+ }
}
} else {
- /* Disconnect tag */
- tag.doAsyncDisconnect();
+ Log.w(TAG, "Failed to connect to tag");
+ nativeTag.doAsyncDisconnect();
}
break;
case MSG_CARD_EMULATION:
@@ -176,8 +216,8 @@
byte[] aid = (byte[]) msg.obj;
/* Send broadcast ordered */
Intent TransactionIntent = new Intent();
- TransactionIntent.setAction(NfcManager.TRANSACTION_DETECTED_ACTION);
- TransactionIntent.putExtra(NfcManager.AID_EXTRA, aid);
+ TransactionIntent.setAction(NfcAdapter.ACTION_TRANSACTION_DETECTED);
+ TransactionIntent.putExtra(NfcAdapter.EXTRA_AID, aid);
Log.d(TAG, "Broadcasting Card Emulation event");
mContext.sendOrderedBroadcast(TransactionIntent,
android.Manifest.permission.NFC_NOTIFY);
@@ -201,7 +241,7 @@
.setAction(INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION);
LlcpLinkIntent.putExtra(
INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA,
- NfcManager.LLCP_LINK_STATE_ACTIVATED);
+ NfcAdapter.LLCP_LINK_STATE_ACTIVATED);
Log.d(TAG, "Broadcasting internal LLCP activation");
mContext.sendBroadcast(LlcpLinkIntent);
}
@@ -223,7 +263,7 @@
LlcpLinkIntent
.setAction(INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION);
LlcpLinkIntent.putExtra(INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA,
- NfcManager.LLCP_LINK_STATE_ACTIVATED);
+ NfcAdapter.LLCP_LINK_STATE_ACTIVATED);
Log.d(TAG, "Broadcasting internal LLCP activation");
mContext.sendBroadcast(LlcpLinkIntent);
}
@@ -235,9 +275,9 @@
/* Broadcast Intent Link LLCP activated */
Log.d(TAG, "LLCP Link Deactivated message");
Intent LlcpLinkIntent = new Intent();
- LlcpLinkIntent.setAction(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION);
- LlcpLinkIntent.putExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA,
- NfcManager.LLCP_LINK_STATE_DEACTIVATED);
+ LlcpLinkIntent.setAction(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED);
+ LlcpLinkIntent.putExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED,
+ NfcAdapter.LLCP_LINK_STATE_DEACTIVATED);
Log.d(TAG, "Broadcasting LLCP deactivation");
mContext.sendOrderedBroadcast(LlcpLinkIntent,
android.Manifest.permission.NFC_LLCP);
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java
index b92783d..47cf45b 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeNfcTag.java
@@ -24,39 +24,48 @@
/**
* Native interface to the NFC tag functions
- *
- * {@hide}
+ *
+ * @hide
*/
public class NativeNfcTag {
- private int mHandle;
+ private int mHandle;
- private String mType;
-
- private byte[] mUid;
+ private String mType;
- public native boolean doConnect();
+ private byte[] mUid;
- public native boolean doDisconnect();
-
- public native void doAsyncDisconnect();
+ public native boolean doConnect();
- public native byte[] doTransceive(byte[] data);
+ public native boolean doDisconnect();
- public native boolean checkNDEF();
-
+ public native void doAsyncDisconnect();
+
+ public native byte[] doTransceive(byte[] data);
+
+ public native boolean checkNDEF();
+
public native byte[] doRead();
public native boolean doWrite(byte[] buf);
- public int getHandle() {
- return mHandle;
- }
-
- public String getType() {
- return mType;
- }
-
- public byte[] getUid() {
- return mUid;
- }
+ private NativeNfcTag() {
+ }
+
+ public NativeNfcTag(int handle, String type, byte[] uid) {
+ mHandle = handle;
+ mType = type;
+ mUid = uid.clone();
+ }
+
+ public int getHandle() {
+ return mHandle;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ public byte[] getUid() {
+ return mUid;
+ }
}
diff --git a/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java b/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java
index 75d25ba..c674309 100644
--- a/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java
+++ b/core/java/com/trustedlogic/trustednfc/android/internal/NativeP2pDevice.java
@@ -24,52 +24,52 @@
/**
* Native interface to the P2P Initiator functions
- *
- * {@hide}
+ *
+ * @hide
*/
public class NativeP2pDevice {
-
- /**
- * Peer-to-Peer Target.
- */
- public static final short MODE_P2P_TARGET = 0x00;
- /**
- * Peer-to-Peer Initiator.
- */
- public static final short MODE_P2P_INITIATOR = 0x01;
+ /**
+ * Peer-to-Peer Target.
+ */
+ public static final short MODE_P2P_TARGET = 0x00;
- /**
- * Invalid target type.
- */
- public static final short MODE_INVALID = 0xff;
+ /**
+ * Peer-to-Peer Initiator.
+ */
+ public static final short MODE_P2P_INITIATOR = 0x01;
- private int mHandle;
+ /**
+ * Invalid target type.
+ */
+ public static final short MODE_INVALID = 0xff;
- private int mMode;
+ private int mHandle;
- private byte[] mGeneralBytes;
+ private int mMode;
- public native byte[] doReceive();
+ private byte[] mGeneralBytes;
- public native boolean doSend(byte[] data);
+ public native byte[] doReceive();
- public native boolean doConnect();
+ public native boolean doSend(byte[] data);
- public native boolean doDisconnect();
+ public native boolean doConnect();
- public native byte[] doTransceive(byte[] data);
-
- public int getHandle() {
- return mHandle;
- }
+ public native boolean doDisconnect();
- public int getMode() {
- return mMode;
- }
+ public native byte[] doTransceive(byte[] data);
- public byte[] getGeneralBytes() {
- return mGeneralBytes;
- }
+ public int getHandle() {
+ return mHandle;
+ }
+
+ public int getMode() {
+ return mMode;
+ }
+
+ public byte[] getGeneralBytes() {
+ return mGeneralBytes;
+ }
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9d914ad..ff079e4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -86,9 +86,10 @@
<protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" />
<protected-broadcast android:name="android.hardware.action.USB_STATE" />
- <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.NDEF_TAG_DISCOVERED" />
- <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.TRANSACTION_DETECTED" />
- <protected-broadcast android:name="com.trustedlogic.trustednfc.android.action.LLCP_LINK_STATE_CHANGED" />
+ <protected-broadcast android:name="android.nfc.action.NDEF_TAG_DISCOVERED" />
+ <protected-broadcast android:name="android.nfc.action.TAG_DISCOVERED" />
+ <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
+ <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
@@ -1332,10 +1333,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name="com.android.internal.app.ExternalMediaFormatActivity"
- android:theme="@style/Theme.Dialog.Alert"
- android:excludeFromRecents="true">
- </activity>
<activity android:name="android.accounts.ChooseAccountActivity"
android:excludeFromRecents="true"
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
index 4f95d50..b9fd0a4 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
index 10a37b7..94e947d 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_background.9.png b/core/res/res/drawable-hdpi/menu_background.9.png
index 1b43435..f4c9e08 100644
--- a/core/res/res/drawable-hdpi/menu_background.9.png
+++ b/core/res/res/drawable-hdpi/menu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png b/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
index ec974d6..a3cec11 100644
--- a/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
+++ b/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_dark.9.png b/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
index 8b5d3d5..442105c 100755
--- a/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_dark.9.png b/core/res/res/drawable-hdpi/popup_center_dark.9.png
index ac1f92df..43d3e1f 100755
--- a/core/res/res/drawable-hdpi/popup_center_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_full_dark.9.png b/core/res/res/drawable-hdpi/popup_full_dark.9.png
index 8f2fdf0..c28465b 100755
--- a/core/res/res/drawable-hdpi/popup_full_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_top_dark.9.png b/core/res/res/drawable-hdpi/popup_top_dark.9.png
index 1108909..31f5f3b 100755
--- a/core/res/res/drawable-hdpi/popup_top_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_on.png b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
index 1e1ab32..150c9fc 100644
--- a/core/res/res/drawable-hdpi/stat_sys_gps_on.png
+++ b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle.png b/core/res/res/drawable-hdpi/text_select_handle.png
deleted file mode 100644
index 80d48ab..0000000
--- a/core/res/res/drawable-hdpi/text_select_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_bottom_dark.9.png b/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
index 76a2a7f..c21f982 100644
--- a/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_bottom_medium.9.png b/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
index dee6d6b..0992135 100755
--- a/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_full_dark.9.png b/core/res/res/drawable-mdpi/popup_full_dark.9.png
index 2305be4..84ba5ca0 100644
--- a/core/res/res/drawable-mdpi/popup_full_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_top_dark.9.png b/core/res/res/drawable-mdpi/popup_top_dark.9.png
index af511f2..2f847ad 100644
--- a/core/res/res/drawable-mdpi/popup_top_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_gps_on.png b/core/res/res/drawable-mdpi/stat_sys_gps_on.png
old mode 100755
new mode 100644
index a2c677d..70354b4
--- a/core/res/res/drawable-mdpi/stat_sys_gps_on.png
+++ b/core/res/res/drawable-mdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle.png b/core/res/res/drawable-mdpi/text_select_handle.png
deleted file mode 100644
index 93a5a15..0000000
--- a/core/res/res/drawable-mdpi/text_select_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left.png b/core/res/res/drawable-mdpi/text_select_handle_left.png
new file mode 100644
index 0000000..40e6ac0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle_left.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_middle.png b/core/res/res/drawable-mdpi/text_select_handle_middle.png
index 201e7f9..9421759d 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_middle.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_middle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right.png b/core/res/res/drawable-mdpi/text_select_handle_right.png
new file mode 100644
index 0000000..9d2d08a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle_right.png
Binary files differ
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index a33851c..e799527 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -63,6 +63,9 @@
<item>@drawable/scrollbar_handle_horizontal</item>
<item>@drawable/scrollbar_handle_vertical</item>
<item>@drawable/spinner_dropdown_background</item>
+ <item>@drawable/text_select_handle_left</item>
+ <item>@drawable/text_select_handle_middle</item>
+ <item>@drawable/text_select_handle_right</item>
<item>@drawable/title_bar</item>
<item>@drawable/title_bar_shadow</item>
<!-- Visual lock screen -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c3cbf07..99fc5c2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -149,9 +149,15 @@
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
+ <!-- Don't name config resources like this. It should look like config_annoyDianne -->
+ <bool name="config_annoy_dianne">true</bool>
+
<!-- If this is true, the screen will come on when you unplug usb/power/whatever. -->
<bool name="config_unplugTurnsOnScreen">false</bool>
+ <!-- If this is true, the screen will fade off. -->
+ <bool name="config_animateScreenLights">true</bool>
+
<!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION -->
<!-- The number of degrees to rotate the display when the keyboard is open. -->
@@ -354,7 +360,7 @@
<integer name="config_datause_notification_type">2</integer>
<!-- Enables SIP on WIFI only -->
- <bool name="config_sip_wifi_only">false</bool>
+ <bool name="config_sip_wifi_only">true</bool>
<!-- Boolean indicating if restoring network selection should be skipped -->
<!-- The restoring is handled by modem if it is true-->
diff --git a/core/tests/coretests/src/com/android/server/MountServiceTests.java b/core/tests/coretests/src/com/android/server/MountServiceTests.java
index 83e9d18..1f8c92e 100644
--- a/core/tests/coretests/src/com/android/server/MountServiceTests.java
+++ b/core/tests/coretests/src/com/android/server/MountServiceTests.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
-import android.os.Environment;
import android.os.FileUtils;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageManager;
@@ -57,17 +56,15 @@
}
}
- private interface CompletableTask {
- public boolean isDone();
- }
+ private static class ObbObserver extends OnObbStateChangeListener {
+ private String path;
- private static class ObbObserver extends OnObbStateChangeListener implements CompletableTask {
- public String path;
- public String state;
+ public int state = -1;
boolean done = false;
@Override
- public void onObbStateChange(String path, String state) {
+ public void onObbStateChange(String path, int state) {
+ Log.d(TAG, "Received message. path=" + path + ", state=" + state);
synchronized (this) {
this.path = path;
this.state = state;
@@ -76,32 +73,43 @@
}
}
+ public String getPath() {
+ assertTrue("Expected ObbObserver to have received a state change.", done);
+ return path;
+ }
+
+ public int getState() {
+ assertTrue("Expected ObbObserver to have received a state change.", done);
+ return state;
+ }
+
public void reset() {
this.path = null;
- this.state = null;
+ this.state = -1;
done = false;
}
public boolean isDone() {
return done;
}
- }
- private boolean waitForCompletion(CompletableTask task) {
- long waitTime = 0;
- synchronized (task) {
- while (!task.isDone() && waitTime < MAX_WAIT_TIME) {
- try {
- task.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- } catch (InterruptedException e) {
- Log.i(TAG, "Interrupted during sleep", e);
+ public boolean waitForCompletion() {
+ long waitTime = 0;
+ synchronized (this) {
+ while (!isDone() && waitTime < MAX_WAIT_TIME) {
+ try {
+ wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ } catch (InterruptedException e) {
+ Log.i(TAG, "Interrupted during sleep", e);
+ }
}
}
- }
- return task.isDone();
+ return isDone();
+ }
}
+
private File getFilePath(String name) {
final File filesDir = mContext.getFilesDir();
final File outFile = new File(filesDir, name);
@@ -128,23 +136,52 @@
}
private void mountObb(StorageManager sm, final int resource, final File file,
- String expectedState) {
+ int expectedState) {
copyRawToFile(resource, file);
- ObbObserver observer = new ObbObserver();
+ final ObbObserver observer = new ObbObserver();
assertTrue("mountObb call on " + file.getPath() + " should succeed",
sm.mountObb(file.getPath(), null, observer));
assertTrue("Mount should have completed",
- waitForCompletion(observer));
+ observer.waitForCompletion());
+
+ if (expectedState == OnObbStateChangeListener.MOUNTED) {
+ assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
+ }
assertEquals("Actual file and resolved file should be the same",
- file.getPath(), observer.path);
+ file.getPath(), observer.getPath());
- assertEquals(expectedState, observer.state);
+ assertEquals(expectedState, observer.getState());
}
- private String checkMountedPath(StorageManager sm, File file) {
+ private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource,
+ final File file) {
+ copyRawToFile(resource, file);
+
+ final ObbObserver observer = new ObbObserver();
+ assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file
+ .getPath(), null, observer));
+
+ return observer;
+ }
+
+ private void waitForObbActionCompletion(final StorageManager sm, final File file,
+ final ObbObserver observer, int expectedState, boolean checkPath) {
+ assertTrue("Mount should have completed", observer.waitForCompletion());
+
+ assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
+
+ if (checkPath) {
+ assertEquals("Actual file and resolved file should be the same", file.getPath(),
+ observer.getPath());
+ }
+
+ assertEquals(expectedState, observer.getState());
+ }
+
+ private String checkMountedPath(final StorageManager sm, final File file) {
final String mountPath = sm.getMountedObbPath(file.getPath());
assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
OBB_MOUNT_PREFIX,
@@ -152,13 +189,21 @@
return mountPath;
}
- private void unmountObb(StorageManager sm, final File outFile) {
- ObbObserver observer = new ObbObserver();
+ private void unmountObb(final StorageManager sm, final File file, int expectedState) {
+ final ObbObserver observer = new ObbObserver();
+
assertTrue("unmountObb call on test1.obb should succeed",
- sm.unmountObb(outFile.getPath(), false, observer));
+ sm.unmountObb(file.getPath(),
+ false, observer));
assertTrue("Unmount should have completed",
- waitForCompletion(observer));
+ observer.waitForCompletion());
+
+ assertEquals(expectedState, observer.getState());
+
+ if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
+ assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath()));
+ }
}
@LargeTest
@@ -167,7 +212,9 @@
final File outFile = getFilePath("test1.obb");
- mountObb(sm, R.raw.test1, outFile, Environment.MEDIA_MOUNTED);
+ mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
+
+ mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
final String mountPath = checkMountedPath(sm, outFile);
final File mountDir = new File(mountPath);
@@ -175,7 +222,7 @@
assertTrue("OBB mounted path should be a directory",
mountDir.isDirectory());
- unmountObb(sm, outFile);
+ unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED);
}
@LargeTest
@@ -184,7 +231,7 @@
final File outFile = getFilePath("test1_nosig.obb");
- mountObb(sm, R.raw.test1_nosig, outFile, Environment.MEDIA_BAD_REMOVAL);
+ mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
assertFalse("OBB should not be mounted",
sm.isObbMounted(outFile.getPath()));
@@ -199,7 +246,8 @@
final File outFile = getFilePath("test1_wrongpackage.obb");
- mountObb(sm, R.raw.test1_wrongpackage, outFile, Environment.MEDIA_BAD_REMOVAL);
+ mountObb(sm, R.raw.test1_wrongpackage, outFile,
+ OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
assertFalse("OBB should not be mounted",
sm.isObbMounted(outFile.getPath()));
@@ -207,4 +255,31 @@
assertNull("OBB's mounted path should be null",
sm.getMountedObbPath(outFile.getPath()));
}
+
+ @LargeTest
+ public void testMountAndUnmountTwoObbs() {
+ StorageManager sm = getStorageManager();
+
+ final File file1 = getFilePath("test1.obb");
+ final File file2 = getFilePath("test2.obb");
+
+ ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1);
+ ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2);
+
+ Log.d(TAG, "Waiting for OBB #1 to complete mount");
+ waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false);
+ Log.d(TAG, "Waiting for OBB #2 to complete mount");
+ waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false);
+
+ final String mountPath1 = checkMountedPath(sm, file1);
+ final File mountDir1 = new File(mountPath1);
+ assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
+
+ final String mountPath2 = checkMountedPath(sm, file2);
+ final File mountDir2 = new File(mountPath2);
+ assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
+
+ unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
+ unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
+ }
}
diff --git a/docs/html/guide/developing/testing/index.jd b/docs/html/guide/developing/testing/index.jd
index ea61cc3..2164705 100644
--- a/docs/html/guide/developing/testing/index.jd
+++ b/docs/html/guide/developing/testing/index.jd
@@ -1,6 +1,5 @@
page.title=Testing Overview
@jd:body
-
<p>
Android includes powerful tools for setting up and running test applications.
Whether you are working in Eclipse with ADT or working from the command line, these tools
@@ -9,7 +8,7 @@
</p>
<p>
If you aren't yet familiar with the Android testing framework, please read the topic
- <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing and Instrumentation</a>
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>
before you get started.
For a step-by-step introduction to Android testing, try the <a
href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">Hello, Testing</a>
diff --git a/docs/html/guide/developing/testing/testing_eclipse.jd b/docs/html/guide/developing/testing/testing_eclipse.jd
index da1c0f0..ba7eaba 100644
--- a/docs/html/guide/developing/testing/testing_eclipse.jd
+++ b/docs/html/guide/developing/testing/testing_eclipse.jd
@@ -1,28 +1,23 @@
page.title=Testing In Eclipse, with ADT
@jd:body
-
<div id="qv-wrapper">
- <div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#CreateTestProjectEclipse">Creating a Test Project</a></li>
- <li><a href="#CreateTestAppEclipse">Creating a Test Application</a></li>
- <li><a href="#RunTestEclipse">Running Tests</a></li>
- </ol>
- </div>
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#CreateTestProjectEclipse">Creating a Test Project</a></li>
+ <li><a href="#CreateTestAppEclipse">Creating a Test Package</a></li>
+ <li><a href="#RunTestEclipse">Running Tests</a></li>
+ </ol>
+ </div>
</div>
<p>
- This topic explains how create and run tests of Android applications in Eclipse with ADT.
-
- with the basic processes for creating and running applications with ADT, as described in
- <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing In Eclipse, with ADT</a>.
-
- Before you read this topic, you should read about how to create a Android application with the
- basic processes for creating and running applications with ADT, as described in
- <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing In Eclipse, with ADT</a>.
- You may also want to read
- <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing and Instrumentation</a>,
- which provides an overview of the Android testing framework.
+ This topic explains how create and run tests of Android applications in Eclipse with ADT.
+ Before you read this topic, you should read about how to create a Android application with the
+ basic processes for creating and running applications with ADT, as described in
+ <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing In Eclipse, with ADT</a>.
+ You may also want to read
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
+ which provides an overview of the Android testing framework.
</p>
<p>
ADT provides several features that help you set up and manage your testing environment
@@ -32,20 +27,20 @@
<li>
It lets you quickly create a test project and link it to the application under test.
When it creates the test project, it automatically inserts the necessary
- <code><instrumentation></code> element in the test application's manifest file.
+ <code><instrumentation></code> element in the test package's manifest file.
</li>
<li>
It lets you quickly import the classes of the application under test, so that your
tests can inspect them.
</li>
<li>
- It lets you create run configurations for your test application and include in
+ It lets you create run configurations for your test package and include in
them flags that are passed to the Android testing framework.
</li>
<li>
- It lets you run your test application without leaving Eclipse. ADT builds both the
- application under test and the test application automatically, installs them if
- necessary to your device or emulator, runs the test application, and displays the
+ It lets you run your test package without leaving Eclipse. ADT builds both the
+ application under test and the test package automatically, installs them if
+ necessary to your device or emulator, runs the test package, and displays the
results in a separate window in Eclipse.
</li>
</ul>
@@ -55,305 +50,452 @@
<a href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other IDEs</a>.
</p>
<h2 id="CreateTestProjectEclipse">Creating a Test Project</h2>
- <p>
+<p>
To set up a test environment for your Android application, you must first create a separate
- application project that holds the test code. The new project follows the directory structure
+ project that holds the test code. The new project follows the directory structure
used for any Android application. It includes the same types of content and files, such as
- source code, resources, a manifest file, and so forth. The test application you
+ source code, resources, a manifest file, and so forth. The test package you
create is connected to the application under test by an
<a href="{@docRoot}guide/topics/manifest/instrumentation-element.html">
<code><instrumentation></code></a> element in its manifest file.
- </p>
- <p>
- The <strong>New Android Test Project</strong> dialog makes it easy for you to generate a
- new test project that has the proper structure, including the
- <code><instrumentation></code> element in the manifest file. You can use the New Android
- Test Project dialog to generate the test project at any time. The dialog appears just after you
- create a new Android main application project, but you can also run it to create a test project
- for a project that you created previously.
- </p>
+</p>
<p>
- To create a test project in Eclipse with ADT:
+ The <em>New Android Test Project</em> dialog makes it easy for you to generate a
+ new test project that has the proper structure, including the
+ <code><instrumentation></code> element in the manifest file. You can use the New
+ Android Test Project dialog to generate the test project at any time. The dialog appears
+ just after you create a new Android main application project, but you can also run it to
+ create a test project for a project that you created previously.
+</p>
+<p>
+ To create a test project in Eclipse with ADT:
</p>
<ol>
- <li>
- In Eclipse, select <strong>File > New > Other</strong>. This
- opens the Select a Wizard dialog.
- </li>
- <li>
- In the dialog, in the Wizards drop-down list,
- find the entry for Android, then click the toggle to the left. Select
- Android Test Project, then at the bottom
- of the dialog click Next. The New Android Test Project wizard appears.
- </li>
- <li>
- Enter a project name. You may use any name, but you may want to
- associate the name with the project name for your Application. One
- way to do this is to take the Application's project name, append the
- string "Test" to it, and then use this as the test case project name.
- </li>
- <li>
- In the Test Target panel, set
- An Existing Android Project, click
- Browse, then select your Android application from
- the list. You now see that the wizard has completed the Test
- Target Package, Application Name, and
- Package Name fields for you (the latter two are in
- the Properties panel).
- </li>
- <li>
- In the Build Target panel, select the Android SDK
- platform that you will use to test your application. Make this the same as the
- build target of the application under test.
- </li>
- <li>
- Click Finish to complete the wizard. If
- Finish is disabled, look
- for error messages at the top of the wizard dialog, and then fix
- any problems.
- </li>
+ <li>
+ In Eclipse, select <strong>File > New > Other</strong>. This opens the <em>Select a
+ Wizard</em> dialog.
+ </li>
+ <li>
+ In the dialog, in the <em>Wizards</em> drop-down list, find the entry for Android, then
+ click the toggle to the left. Select <strong>Android Test Project</strong>, then at the
+ bottom of the dialog click <strong>Next</strong>. The <em>New Android Test Project</em>
+ wizard appears.
+ </li>
+ <li>
+ Next to <em>Test Project Name</em>, enter a name for the project. You may use any name,
+ but you may want to associate the name with the project name for the application under test.
+ One way to do this is to take the application's project name, append the string "Test" to
+ it, and then use this as the test package project name.
+ <p>
+ The name becomes part of the suggested project path, but you can change this in the
+ next step.
+ </p>
+ </li>
+ <li>
+ In the <em>Content</em> panel, examine the suggested path to the project.
+ If <em>Use default location</em> is set, then the wizard will suggest a path that is
+ a concatenation of the workspace path and the project name you entered. For example,
+ if your workspace path is <code>/usr/local/workspace</code> and your project name is
+ <code>MyTestApp</code>, then the wizard will suggest
+ <code>/usr/local/workspace/MyTestApp</code>. To enter your own
+ choice for a path, unselect <em>Use default location</em>, then enter or browse to the
+ path where you want your project.
+ <p>
+ To learn more about choosing the location of test projects, please read
+ <a href="{@docRoot}guide/topics/testing/testing_android.html#TestProjectPaths">
+ Testing Fundamentals</a>.
+ </p>
+ </li>
+ <li>
+ In the Test Target panel, set An Existing Android Project, click Browse, then select your
+ Android application from the list. You now see that the wizard has completed the Test
+ Target Package, Application Name, and Package Name fields for you (the latter two are in
+ the Properties panel).
+ </li>
+ <li>
+ In the Build Target panel, select the Android SDK platform that the application under test
+ uses.
+ </li>
+ <li>
+ Click Finish to complete the wizard. If Finish is disabled, look for error messages at the
+ top of the wizard dialog, and then fix any problems.
+ </li>
+</ol>
+<h2 id="CreateTestAppEclipse">Creating a Test Package</h2>
+<p>
+ Once you have created a test project, you populate it with a test package. This package does not
+ require an Activity, although you can define one if you wish. Although your test package can
+ combine Activity classes, test case classes, or ordinary classes, your main test case
+ should extend one of the Android test case classes or JUnit classes, because these provide the
+ best testing features.
+</p>
+<p>
+ Test packages do not need to have an Android GUI. When you run the package in
+ Eclipse with ADT, its results appear in the JUnit view. Running tests and seeing the results is
+ described in more detail in the section <a href="#RunTestEclipse">Running Tests</a>.
+</p>
+
+<p>
+ To create a test package, start with one of Android's test case classes defined in
+ {@link android.test android.test}. These extend the JUnit
+ {@link junit.framework.TestCase TestCase} class. The Android test classes for Activity objects
+ also provide instrumentation for testing an Activity. To learn more about test case
+ classes, please read the topic <a href="{@docRoot}guide/topics/testing/testing_android.html">
+ Testing Fundamentals</a>.
+</p>
+<p>
+ Before you create your test package, you choose the Java package identifier you want to use
+ for your test case classes and the Android package name you want to use. To learn more
+ about this, please read
+ <a href="{@docRoot}guide/topics/testing/testing_android.html#PackageNames">
+ Testing Fundamentals</a>.
+</p>
+<p>
+ To add a test case class to your project:
+</p>
+<ol>
+ <li>
+ In the <em>Project Explorer</em> tab, open your test project, then open the <em>src</em>
+ folder.
+ </li>
+ <li>
+ Find the Java package identifier set by the projection creation wizard. If you haven't
+ added classes yet, this node won't have any children, and its icon will not be filled in.
+ If you want to change the identifier value, right-click the identifier and select
+ <strong>Refactor</strong> > <strong>Rename</strong>, then enter the new name.
+ </li>
+ <li>
+ When you are ready, right-click the Java package identifier again and select
+ <strong>New</strong> > <strong>Class</strong>. This displays the <em>New Java Class</em>
+ dialog, with the <em>Source folder</em> and <em>Package</em> values already set.
+ </li>
+ <li>
+ In the <em>Name</em> field, enter a name for the test case class. One way to choose a
+ class name is to append the string "Test" to the class of the component you are testing.
+ For example, if you are testing the class MyAppActivity, your test case class
+ name would be MyAppActivityTest. Leave the modifiers set to <em>public</em>.
+ </li>
+ <li>
+ In the <em>Superclass</em> field, enter the name of the Android test case class you
+ are extending. You can also browse the available classes.
+ </li>
+ <li>
+ In <em>Which method stubs would you like to create?</em>, unset all the options, then
+ click <strong>Finish</strong>. You will set up the constructor manually.
+ </li>
+ <li>
+ Your new class appears in a new Java editor pane.
+ </li>
</ol>
<p>
-
-</p>
-<h2 id="CreateTestAppEclipse">Creating a Test Application</h2>
-<p>
- Once you have created a test project, you populate it with a test
- Android application. This application does not require an {@link android.app.Activity Activity},
- although you can define one if you wish. Although your test application can
- combine Activities, Android test class extensions, JUnit extensions, or
- ordinary classes, you should extend one of the Android test classes or JUnit classes,
- because these provide the best testing features.
+ You now have to ensure that the constructor is set up correctly. Create a constructor for your
+ class that has no arguments; this is required by JUnit. As the first statement in this
+ constructor, add a call to the base class' constructor. Each base test case class has its
+ own constructor signature. Refer to the class documentation in the documentation for
+ {@link android.test} for more information.
</p>
<p>
- Test applications do not have an Android GUI. Instead, when you run the application in
- Eclipse with ADT, its results appear in the JUnit view. If you run
- your tests with {@link android.test.InstrumentationTestRunner InstrumentationTestRunner} (or a related test runner),
- then it will run all the methods in each class. You can modify this behavior
- by using the {@link junit.framework.TestSuite TestSuite} class.
-</p>
-
-<p>
- To create a test application, start with one of Android's test classes in the Java package {@link android.test android.test}.
- These extend the JUnit {@link junit.framework.TestCase TestCase} class. With a few exceptions, the Android test classes
- also provide instrumentation for testing.
-</p>
-<p>
- For test classes that extend {@link junit.framework.TestCase TestCase}, you probably want to override
- the <code>setUp()</code> and <code>tearDown()</code> methods:
+ To control your test environment, you will want to override the <code>setUp()</code> and
+ <code>tearDown()</code> methods:
</p>
<ul>
- <li>
- <code>setUp()</code>: This method is invoked before any of the test methods in the class.
- Use it to set up the environment for the test. You can use <code>setUp()</code>
- to instantiate a new <code>Intent</code> object with the action <code>ACTION_MAIN</code>. You can
- then use this intent to start the Activity under test.
- <p class="note"><strong>Note:</strong> If you override this method, call
- <code>super.setUp()</code> as the first statement in your code.
- </p>
- </li>
- <li>
- <code>tearDown()</code>: This method is invoked after all the test methods in the class. Use
- it to do garbage collection and re-setting before moving on to the next set of tests.
- <p class="note"><strong>Note:</strong> If you override this method, you must call
- <code>super.tearDown()</code> as the <em>last</em> statement in your code.</p>
- </li>
+ <li>
+ <code>setUp()</code>: This method is invoked before any of the test methods in the class.
+ Use it to set up the environment for the test (the test fixture. You can use
+ <code>setUp()</code> to instantiate a new Intent with the action <code>ACTION_MAIN</code>.
+ You can then use this intent to start the Activity under test.
+ </li>
+ <li>
+ <code>tearDown()</code>: This method is invoked after all the test methods in the class. Use
+ it to do garbage collection and to reset the test fixture.
+ </li>
</ul>
<p>
- Another useful convention is to add the method <code>testPreConditions()</code> to your test
- class. Use this method to test that the application under test is initialized correctly. If this
- test fails, you know that that the initial conditions were in error. When this happens, further test
- results are suspect, regardless of whether or not the tests succeeded.
+ Another useful convention is to add the method <code>testPreconditions()</code> to your test
+ class. Use this method to test that the application under test is initialized correctly. If this
+ test fails, you know that that the initial conditions were in error. When this happens, further
+ test results are suspect, regardless of whether or not the tests succeeded.
</p>
<p>
- The Resources tab contains an <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
- tutorial with more information about creating test classes and methods.
+ The Resources tab contains an
+ <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
+ tutorial with more information about creating test classes and methods.
</p>
<h2 id="RunTestEclipse">Running Tests</h2>
+ <div class="sidebox-wrapper">
+ <div class="sidebox">
+ <h2>Running tests from the command line</h2>
+ <p>
+ If you've created your tests in Eclipse, you can still run your tests and test
+ suites by using command-line tools included with the Android SDK. You may want
+ to do this, for example, if you have a large number of tests to run, if you
+ have a large test case, or if you want a fine level of control over which
+ tests are run at a particular time.
+ </p>
+ <p>
+ To run tests created in Eclipse with ADT with command-line tools, you must first
+ install additional files into the test project using the <code>android</code>
+ tool's "create test-project" option. To see how to do this, read
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html#CreateProject">
+ Testing in Other IDEs</a>.
+ </p>
+ </div>
+ </div>
+<p>
+ When you run a test package in Eclipse with ADT, the output appears in the Eclipse JUnit view.
+ You can run the entire test package or one test case class. To do run tests, Eclipse runs the
+ <code>adb</code> command for running a test package, and displays the output, so there is no
+ difference between running tests inside Eclipse and running them from the command line.
+</p>
+<p>
+ As with any other package, to run a test package in Eclipse with ADT you must either attach a
+ device to your computer or use the Android emulator. If you use the emulator, you must have an
+ Android Virtual Device (AVD) that uses the same target as the test package.
+</p>
+<p>
+ To run a test in Eclipse, you have two choices:</p>
+<ul>
+ <li>
+ Run a test just as you run an application, by selecting
+ <strong>Run As... > Android JUnit Test</strong> from the project's context menu or
+ from the main menu's <strong>Run</strong> item.
+ </li>
+ <li>
+ Create an Eclipse run configuration for your test project. This is useful if you want
+ multiple test suites, each consisting of selected tests from the project. To run
+ a test suite, you run the test configuration.
+ <p>
+ Creating and running test configurations is described in the next section.
+ </p>
+ </li>
+</ul>
+<p>
+ To create and run a test suite using a run configuration:
+</p>
+<ol>
+ <li>
+ In the Package Explorer, select the test project, then from the main menu, select
+ <strong>Run > Run Configurations...</strong>. The Run Configurations dialog appears.
+ </li>
+ <li>
+ In the left-hand pane, find the Android JUnit Test entry. In the right-hand pane, click the
+ Test tab. The Name: text box shows the name of your project. The Test class: dropdown box
+ shows one of the test classes in your project.
+ </li>
+ <li>
+ To run one test class, click Run a single test, then enter your project name in the
+ Project: text box and the class name in the Test class: text box.
+ <p>
+ To run all the test classes, click Run all tests in the selected project or package,
+ then enter the project or package name in the text box.
+ </p>
+ </li>
+ <li>
+ Now click the Target tab.
+ <ul>
+ <li>
+ Optional: If you are using the emulator, click Automatic, then in the Android
+ Virtual Device (AVD) selection table, select an existing AVD.
+ </li>
+ <li>
+ In the Emulator Launch Parameters pane, set the Android emulator flags you want to
+ use. These are documented in the topic
+ <a href="{@docRoot}guide/developing/tools/emulator.html#startup-options">
+ Android Emulator</a>.
+ </li>
+ </ul>
+ </li>
+ <li>
+ Click the Common tab. In the Save As pane, click Local to save this run configuration
+ locally, or click Shared to save it to another project.
+ </li>
+ <li>
+ Optional: Add the configuration to the Run toolbar and the <strong>Favorites</strong>
+ menu: in the Display in Favorites pane click the checkbox next to Run.
+ </li>
+ <li>
+ Optional: To add this configuration to the <strong>Debug</strong> menu and toolbar, click
+ the checkbox next to Debug.
+ </li>
+ <li>
+ To save your settings, click Close.<br/>
+ <p class="note"><strong>Note:</strong>
+ Although you can run the test immediately by clicking Run, you should save the test
+ first and then run it by selecting it from the Eclipse standard toolbar.
+ </p>
+ </li>
+ <li>
+ On the Eclipse standard toolbar, click the down arrow next to the green Run arrow. This
+ displays a menu of saved Run and Debug configurations.
+ </li>
+ <li>
+ Select the test run configuration you just created. The test starts.
+ </li>
+</ol>
+<p>
+ The progress of your test appears in the Console view as a series of messages. Each message is
+ preceded by a timestamp and the <code>.apk</code> filename to which it applies. For example,
+ this message appears when you run a test to the emulator, and the emulator is not yet started:
+</p>
<div class="sidebox-wrapper">
<div class="sidebox">
- <h2>Running tests from the command line</h2>
- <p>
- If you've created your tests in Eclipse, you can still run your tests and test
- suites by using command-line tools included with the Android SDK. You may want to
- do this, for example, if you have a large number of tests to run, if you have a
- large test case, or if you want a fine level of control over which tests are run at
- a particular time.
- </p>
- <p>
- To run tests created in Eclipse with ADT with command-line tools, you must first
- install additional files into the test project using the <code>android</code> tool's
- "create test-project" option. To see how to do this, read the section
- <a href="{@docRoot}guide/developing/testing/testing_otheride.html#CreateProject">
- Creating a test project</a> in the topic
- <a href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other
- IDEs</a>.
- </p>
+ <h2>Message Examples</h2>
+ <p>
+ The examples shown in this section come from the
+ <a href="{@docRoot}resources/samples/SpinnerTest/index.html">SpinnerTest</a>
+ sample test package, which tests the
+ <a href="{@docRoot}resources/samples/Spinner/index.html">Spinner</a>
+ sample application. This test package is also featured in the
+ <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
+ tutorial.
+ </p>
</div>
</div>
+<pre>
+ [<em>yyyy-mm-dd hh:mm:ss</em> - <em>testfile</em>] Waiting for HOME ('android.process.acore') to be launched...
+</pre>
<p>
- When you run a test application in Eclipse with ADT, the output appears in
- an Eclipse view panel. You can run the entire test application, one class, or one
- method of a class. To do this, Eclipse runs the <code>adb</code> command for running a test application, and
- displays the output, so there is no difference between running tests inside Eclipse and running them from the command line.
+ In the following description of these messages, <code><em>devicename</em></code> is the name of
+ the device or emulator you are using to run the test, and <code><em>port</em></code> is the
+ port number for the device. The name and port number are in the format used by the
+ <code><a href="{@docRoot}guide/developing/tools/adb.html#devicestatus">adb devices</a></code>
+ command. Also, <code><em>testfile</em></code> is the <code>.apk</code> filename of the test
+ package you are running, and <em>appfile</em> is the filename of the application under test.
</p>
-<p>
- As with any other application, to run a test application in Eclipse with ADT you must either attach a device to your
- computer or use the Android emulator. If you use the emulator, you must have an Android Virtual Device (AVD) that uses
- the same target
-</p>
-<p>
- To run a test in Eclipse, you have two choices:</p>
-<ol>
- <li>
- Run a test just as you run an application, by selecting
- <strong>Run As... > Android JUnit Test</strong> from the project's context menu or
- from the main menu's <strong>Run</strong> item.
- </li>
- <li>
- Create an Eclipse run configuration for your test project. This is useful if you want multiple test suites, each consisting of selected tests from the project. To run
- a test suite, you run the test configuration.
- <p>
- Creating and running test configurations is described in the next section.
- </p>
- </li>
-</ol>
-<p>To create and run a test suite using a run configuration:</p>
-<ol>
- <li>
- In the Package Explorer, select the test
- project, then from the main menu, select
- <strong>Run > Run Configurations...</strong>. The
- Run Configurations dialog appears.
- </li>
- <li>
- In the left-hand pane, find the
- Android JUnit Test entry.
- In the right-hand pane, click the Test tab.
- The Name: text box
- shows the name of your project. The
- Test class: dropdown box shows one your project's classes
- test classes in your project.
- </li>
- <li>
- To run one test class, click Run a single test, then enter your project
- name in the Project: text box and the class name in the
- Test class: text box.
- <p>
- To run all the test classes,
- click Run all tests in the selected project or package,
- then enter the project or package name in the text box.
- </p>
- </li>
- <li>
- Now click the Target tab.
- <ul>
- <li>
- Optional: If you are using the emulator, click
- Automatic, then in the Android Virtual Device (AVD)
- selection table, select an existing AVD.
- </li>
- <li>
- In the Emulator Launch Parameters pane, set the
- Android emulator flags you want to use. These are documented in the topic
- <a href="{@docRoot}guide/developing/tools/emulator.html#startup-options">Emulator Startup Options</a>.
- </li>
- </ul>
- <li>
- Click the Common tab. In the
- Save As pane, click Local to save
- this run configuration locally, or click Shared to
- save it to another project.
- </li>
- <li>
- Optional: Add the configuration to the Run toolbar and the <strong>Favorites</strong>
- menu: in the Display in Favorites pane
- click the checkbox next to Run.
- </li>
- <li>
- Optional: To add this configuration to the <strong>Debug</strong> menu and toolbar, click
- the checkbox next to Debug.
- </li>
- <li>
- To save your settings, click Close.<br/>
- <p class="note"><strong>Note:</strong> Although you can run the test immediately by
- clicking Run, you should save the test first and then
- run it by selecting it from the Eclipse standard toolbar.</p>
- </li>
- <li>
- On the Eclipse standard toolbar, click the down arrow next to the
- green Run arrow. This displays a menu of saved Run and Debug
- configurations.
- </li>
- <li>
- Select the test run configuration you just created.
- </li>
- <li>
- The progress of your test appears in the Console view.
- You should see the following messages, among others:
- <ul>
- <li>
- <code>Performing Android.test.InstrumentationTestRunner JUnit launch</code><br>
- The class name that proceeds "JUnit" depends on the Android instrumentation
- class you have chosen.
- </li>
- <li>
- If you are using an emulator and you have not yet started it, then you will see
+<ul>
+ <li>
+ If you are using an emulator and you have not yet started it, then Eclipse
+ first starts the emulator. When this is complete, you see
the message:
<p>
- <code>Automatic Target Mode: launching new emulator with compatible
- AVD <em>avdname</em></code><br>(where <em>avdname</em> is the name of
- the AVD you are using.)
+ <code>HOME is up on device '<em>devicename</em>-<em>port</em>'</code>
</p>
- </li>
- <li>
- If you have not already installed your test application, then you will see
+ </li>
+ <li>
+ If you have not already installed your test package, then you see
the message:
<p>
- <code>Uploading <em>testclass</em>.apk onto device '<em>device-id</em>'</code><br>
- where <em>testclass</em> is the name of your unit test class and <em>device-id</em>
- is the name and port for your test device or emulator, followed by the message <code>Installing <em>testclass</em>.apk</code>
+ <code>Uploading <em>testfile</em> onto device '<em>devicename</em>-<em>port</em>'
+ </code>
</p>
- </li>
- <li>
- <code>Launching instrumentation Android.test.InstrumentationTestRunner on device <em>device-id</em></code>.<br>
- This indicates that Android's Instrumentation system is now testing your code. Again, the
- instrumentation class name depends on the Android instrumentation class you have chosen.
- </li>
- <li>
- <code>Test run complete</code>.<br> When you see this, your unit tests have finished.
- </li>
- </ul>
-</ol>
+ <p>
+ then the message <code>Installing <em>testfile</em></code>.
+ </p>
+ <p>
+ and finally the message <code>Success!</code>
+ </p>
+ </li>
+</ul>
<p>
- The test results appear in the JUnit view. This is divided into an upper summary pane,
- and a lower stack trace pane.
+ The following lines are an example of this message sequence:
+</p>
+<code>
+[2010-07-01 12:44:40 - MyTest] HOME is up on device 'emulator-5554'<br>
+[2010-07-01 12:44:40 - MyTest] Uploading MyTest.apk onto device 'emulator-5554'<br>
+[2010-07-01 12:44:40 - MyTest] Installing MyTest.apk...<br>
+[2010-07-01 12:44:49 - MyTest] Success!<br>
+</code>
+<br>
+<ul>
+ <li>
+ Next, if you have not yet installed the application under test to the device or
+ emulator, you see the message
+ <p>
+ <code>Project dependency found, installing: <em>appfile</em></code>
+ </p>
+ <p>
+ then the message <code>Uploading <em>appfile</em></code> onto device
+ '<em>devicename</em>-<em>port</em>'
+ </p>
+ <p>
+ then the message <code>Installing <em>appfile</em></code>
+ </p>
+ <p>
+ and finally the message <code>Success!</code>
+ </p>
+ </li>
+</ul>
+<p>
+ The following lines are an example of this message sequence:
+</p>
+<code>
+[2010-07-01 12:44:49 - MyTest] Project dependency found, installing: MyApp<br>
+[2010-07-01 12:44:49 - MyApp] Uploading MyApp.apk onto device 'emulator-5554'<br>
+[2010-07-01 12:44:49 - MyApp] Installing MyApp.apk...<br>
+[2010-07-01 12:44:54 - MyApp] Success!<br>
+</code>
+<br>
+<ul>
+ <li>
+ Next, you see the message
+ <code>Launching instrumentation <em>instrumentation_class</em> on device
+ <em>devicename</em>-<em>port</em></code>
+ <p>
+ <code>instrumentation_class</code> is the fully-qualified class name of the
+ instrumentation test runner you have specified (usually
+ {@link android.test.InstrumentationTestRunner}.
+ </p>
+ </li>
+ <li>
+ Next, as {@link android.test.InstrumentationTestRunner} builds a list of tests to run,
+ you see the message
+ <p>
+ <code>Collecting test information</code>
+ </p>
+ <p>
+ followed by
+ </p>
+ <p>
+ <code>Sending test information to Eclipse</code>
+ </p>
+ </li>
+ <li>
+ Finally, you see the message <code>Running tests</code>, which indicates that your tests
+ are running. At this point, you should start seeing the test results in the JUnit view.
+ When the tests are finished, you see the console message <code>Test run complete</code>.
+ This indicates that your tests are finished.
+ </li>
+</ul>
+<p>
+ The following lines are an example of this message sequence:
+</p>
+<code>
+[2010-01-01 12:45:02 - MyTest] Launching instrumentation android.test.InstrumentationTestRunner on device emulator-5554<br>
+[2010-01-01 12:45:02 - MyTest] Collecting test information<br>
+[2010-01-01 12:45:02 - MyTest] Sending test information to Eclipse<br>
+[2010-01-01 12:45:02 - MyTest] Running tests...<br>
+[2010-01-01 12:45:22 - MyTest] Test run complete<br>
+</code>
+<br>
+<p>
+ The test results appear in the JUnit view. This is divided into an upper summary pane,
+ and a lower stack trace pane.
</p>
<p>
- The upper pane contains test information. In the pane's header, you see the following
- information:
+ The upper pane contains test information. In the pane's header, you see the following
+ information:
</p>
- <ul>
- <li>
- Total time elapsed for the test application (labeled Finished after <em>x</em> seconds).
- </li>
- <li>
- Number of runs (Runs:) - the number of tests in the entire test class.
- </li>
- <li>
- Number of errors (Errors:) - the number of program errors and exceptions encountered
- during the test run.
- </li>
- <li>
- Number of failures (Failures:) - the number of test failures encountered during the test
- run. This is the number of assertion failures. A test can fail even if the program does
- not encounter an error.
- </li>
- <li>
- A progress bar. The progress bar extends from left to right as the tests run. If all the
- tests succeed, the bar remains green. If a test fails, the bar turns from green to red.
- </li>
- </ul>
+<ul>
+ <li>
+ Total time elapsed for the test package (labeled Finished after <em>x</em> seconds).
+ </li>
+ <li>
+ Number of runs (Runs:) - the number of tests in the entire test class.
+ </li>
+ <li>
+ Number of errors (Errors:) - the number of program errors and exceptions encountered
+ during the test run.
+ </li>
+ <li>
+ Number of failures (Failures:) - the number of test failures encountered during the test
+ run. This is the number of assertion failures. A test can fail even if the program does
+ not encounter an error.
+ </li>
+ <li>
+ A progress bar. The progress bar extends from left to right as the tests run. If all the
+ tests succeed, the bar remains green. If a test fails, the bar turns from green to red.
+ </li>
+</ul>
<p>
The body of the upper pane contains the details of the test run. For each test case class
that was run, you see a line with the class name. To look at the results for the individual
@@ -363,8 +505,30 @@
pane and moves the focus to the first line of the test method.
</p>
<p>
+ The results of a successful test are shown in
+ <a href="#TestResults">Figure 1. Messages for a successful test</a>:
+</p>
+<a href="{@docRoot}images/testing/eclipse_test_results.png">
+ <img src="{@docRoot}images/testing/eclipse_test_results.png"
+ alt="Messages for a successful test" height="327px" id="TestResults"/>
+</a>
+<p class="img-caption">
+ <strong>Figure 1.</strong> Messages for a successful test
+</p>
+<p>
The lower pane is for stack traces. If you highlight a failed test in the upper pane, the
lower pane contains a stack trace for the test. If a line corresponds to a point in your
test code, you can double-click it to display the code in an editor view pane, with the
line highlighted. For a successful test, the lower pane is empty.
</p>
+<p>
+ The results of a failed test are shown in
+ <a href="#FailedTestResults">Figure 2. Messages for a test failure</a>
+</p>
+<a href="{@docRoot}images/testing/eclipse_test_run_failure.png">
+ <img src="{@docRoot}images/testing/eclipse_test_run_failure.png"
+ alt="Messages for a test failure" height="372px" id="TestRun"/>
+</a>
+<p class="img-caption">
+ <strong>Figure 2.</strong> Messages for a test failure
+</p>
diff --git a/docs/html/guide/developing/testing/testing_otheride.jd b/docs/html/guide/developing/testing/testing_otheride.jd
index 2bdf4d0..523a8e5 100644
--- a/docs/html/guide/developing/testing/testing_otheride.jd
+++ b/docs/html/guide/developing/testing/testing_otheride.jd
@@ -2,122 +2,128 @@
@jd:body
<div id="qv-wrapper">
- <div id="qv">
- <h2>In this document</h2>
- <ol>
- <li>
- <a href="#CreateTestProjectCommand">Working with Test Projects</a>
- <ol>
- <li>
- <a href="#CreateTestProject">Creating a test project</a>
- </li>
- <li>
- <a href="#UpdateTestProject">Updating a test project</a>
- </li>
- </ol>
- </li>
- <li>
- <a href="#CreateTestApp">Creating a Test Application</a>
- </li>
- <li>
- <a href="#RunTestsCommand">Running Tests</a>
- <ol>
- <li>
- <a href="#RunTestsAnt">Quick build and run with Ant</a>
- </li>
- <li>
- <a href="#RunTestsDevice">Running tests on a device or emulator</a>
- </li>
- </ol>
- </li>
- <li>
- <a href="#AMSyntax">Using the Instrument Command</a>
- <ol>
- <li>
- <a href="#AMOptionsSyntax">Instrument options</a>
- </li>
- <li>
- <a href="#RunTestExamples">Instrument examples</a>
- </li>
- </ol>
- </li>
-
- </ol>
- <h2>See Also</h2>
- <ol>
- <li>
- <a
- href="{@docRoot}guide/topics/testing/testing_android.html">Testing and Instrumentation</a>
- </li>
- <li>
- <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
- </li>
- <li>
- <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
- </li>
- </ol>
- </div>
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li>
+ <a href="#CreateTestProjectCommand">Working with Test Projects</a>
+ <ol>
+ <li>
+ <a href="#CreateTestProject">Creating a test project</a>
+ </li>
+ <li>
+ <a href="#UpdateTestProject">Updating a test project</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#CreateTestApp">Creating a Test Package</a>
+ </li>
+ <li>
+ <a href="#RunTestsCommand">Running Tests</a>
+ <ol>
+ <li>
+ <a href="#RunTestsAnt">Quick build and run with Ant</a>
+ </li>
+ <li>
+ <a href="#RunTestsDevice">Running tests on a device or emulator</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#AMSyntax">Using the Instrument Command</a>
+ <ol>
+ <li>
+ <a href="#AMOptionsSyntax">Instrument options</a>
+ </li>
+ <li>
+ <a href="#RunTestExamples">Instrument examples</a>
+ </li>
+ </ol>
+ </li>
+ </ol>
+ <h2>See Also</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">
+ Testing Fundamentals</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
+ </li>
+ </ol>
+ </div>
</div>
<p>
- This document describes how to create and run tests directly from the command line.
- You can use the techniques described here if you are developing in an IDE other than Eclipse
- or if you prefer to work from the command line. This document assumes that you already know how
- to create a Android application in your programming environment. Before you start this
- document, you should read the document <a
- href="{@docRoot}guide/topics/testing/testing_android.html">Testing and Instrumentation</a>,
- which provides an overview of Android testing.
+ This document describes how to create and run tests directly from the command line.
+ You can use the techniques described here if you are developing in an IDE other than Eclipse
+ or if you prefer to work from the command line. This document assumes that you already know how
+ to create a Android application in your programming environment. Before you start this
+ document, you should read the topic
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
+ which provides an overview of Android testing.
</p>
<p>
- If you are developing in Eclipse with ADT, you can set up and run your tests
-directly in Eclipse. For more information, please read <a
- href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in Eclipse, with ADT</a>.
+ If you are developing in Eclipse with ADT, you can set up and run your tests
+ directly in Eclipse. For more information, please read
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a>.
</p>
<h2 id="CreateTestProjectCommand">Working with Test Projects</h2>
<p>
- You use the <code>android</code> tool to create test projects.
- You also use <code>android</code> to convert existing test code into an Android test project,
- or to add the <code>run-tests</code> Ant target to an existing Android test project.
- These operations are described in more detail in the section <a
- href="#UpdateTestProject">Updating a test project</a>.
- The <code>run-tests</code> target is described in <a
- href="#RunTestsAnt">Quick build and run with Ant</a>.
+ You use the <code>android</code> tool to create test projects.
+ You also use <code>android</code> to convert existing test code into an Android test project,
+ or to add the <code>run-tests</code> Ant target to an existing Android test project.
+ These operations are described in more detail in the section <a href="#UpdateTestProject">
+ Updating a test project</a>. The <code>run-tests</code> target is described in
+ <a href="#RunTestsAnt">Quick build and run with Ant</a>.
</p>
<h3 id="CreateTestProject">Creating a test project</h3>
<p>
- To create a test project with the <code>android</code> tool, enter:
-<pre>android create test-project -m <main_path> -n <project_name> -p <test_path></pre>
+ To create a test project with the <code>android</code> tool, enter:
+</p>
+<pre>
+android create test-project -m <main_path> -n <project_name> -p <test_path>
+</pre>
<p>
- You must supply all the flags. The following table explains them in detail:
+ You must supply all the flags. The following table explains them in detail:
</p>
<table>
- <tr>
- <th>Flag</th>
- <th>Value</th>
- <th>Description</th>
- <tr>
- <td><code>-m, --main</code></td>
- <td>
- Path to the project of the application under test, relative to the test application
- directory.
- </td>
- <td>
- For example, if the application under test is in <code>source/HelloAndroid</code>, and you
- want to create the test project in <code>source/HelloAndroidTest</code>, then the value of
- <code>--main</code> should be <code>../HelloAndroid</code>.
+ <tr>
+ <th>Flag</th>
+ <th>Value</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td><code>-m, --main</code></td>
+ <td>
+ Path to the project of the application under test, relative to the test package
+ directory.
</td>
- <tr>
- <td><code>-n, --name</code></td>
- <td>Name that you want to give the test project.</td>
- <td> </td>
- </tr>
- <tr>
- <td><code>-p, --path</code></td>
- <td>Directory in which you want to create the new test project.</td>
- <td>
- The <code>android</code> tool creates the test project files and directory structure in this
- directory. If the directory does not exist, <code>android</code> creates it.
- </td>
- </tr>
+ <td>
+ For example, if the application under test is in <code>source/HelloAndroid</code>, and
+ you want to create the test project in <code>source/HelloAndroidTest</code>, then the
+ value of <code>--main</code> should be <code>../HelloAndroid</code>.
+ <p>
+ To learn more about choosing the location of test projects, please read
+ <a href="{@docRoot}guide/topics/testing/testing_android.html#TestProjects">
+ Testing Fundamentals</a>.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>-n, --name</code></td>
+ <td>Name that you want to give the test project.</td>
+ <td> </td>
+ </tr>
+ <tr>
+ <td><code>-p, --path</code></td>
+ <td>Directory in which you want to create the new test project.</td>
+ <td>
+ The <code>android</code> tool creates the test project files and directory structure
+ in this directory. If the directory does not exist, <code>android</code> creates it.
+ </td>
+ </tr>
</table>
<p>
If the operation is successful, <code>android</code> lists to STDOUT the names of the files
@@ -135,11 +141,10 @@
are testing and control it with instrumentation.
</p>
<p>
- For example, suppose you create the <a
- href="{@docRoot}resources/tutorials/hello-world.html">Hello, World</a> tutorial application
- in the directory <code>~/source/HelloAndroid</code>. In the tutorial, this application uses the
- package name <code>com.example.helloandroid</code> and the activity name
- <code>HelloAndroid</code>. You can to create the test for this in
+ For example, suppose you create the <a href="{@docRoot}resources/tutorials/hello-world.html">
+ Hello, World</a> tutorial application in the directory <code>~/source/HelloAndroid</code>.
+ In the tutorial, this application uses the package name <code>com.example.helloandroid</code>
+ and the activity name <code>HelloAndroid</code>. You can to create the test for this in
<code>~/source/HelloAndroidTest</code>. To do so, you enter:
</p>
<pre>
@@ -196,7 +201,7 @@
<p class="note">
<strong>Note:</strong> If you change the Android package name of the application under test,
you must <em>manually</em> change the value of the <code><android:targetPackage></code>
- attribute within the <code>AndroidManifest.xml</code> file of the test application.
+ attribute within the <code>AndroidManifest.xml</code> file of the test package.
Running <code>android update test-project</code> does not do this.
</p>
<p>
@@ -205,38 +210,38 @@
<pre>android update-test-project -m <main_path> -p <test_path></pre>
<table>
-<tr>
- <th>Flag</th>
- <th>Value</th>
- <th>Description</th>
-</tr>
-<tr>
- <td><code>-m, --main</code></td>
- <td>The path to the project of the application under test, relative to the test project</td>
- <td>
- For example, if the application under test is in <code>source/HelloAndroid</code>, and
- the test project is in <code>source/HelloAndroidTest</code>, then the value for
- <code>--main</code> is <code>../HelloAndroid</code>.
- </td>
-</tr>
-<tr>
- <td><code>-p, --path</code></td>
- <td>The of the test project.</td>
- <td>
- For example, if the test project is in <code>source/HelloAndroidTest</code>, then the
- value for <code>--path</code> is <code>HelloAndroidTest</code>.
- </td>
-</tr>
+ <tr>
+ <th>Flag</th>
+ <th>Value</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td><code>-m, --main</code></td>
+ <td>The path to the project of the application under test, relative to the test project</td>
+ <td>
+ For example, if the application under test is in <code>source/HelloAndroid</code>, and
+ the test project is in <code>source/HelloAndroidTest</code>, then the value for
+ <code>--main</code> is <code>../HelloAndroid</code>.
+ </td>
+ </tr>
+ <tr>
+ <td><code>-p, --path</code></td>
+ <td>The of the test project.</td>
+ <td>
+ For example, if the test project is in <code>source/HelloAndroidTest</code>, then the
+ value for <code>--path</code> is <code>HelloAndroidTest</code>.
+ </td>
+ </tr>
</table>
<p>
If the operation is successful, <code>android</code> lists to STDOUT the names of the files
and directories it has created.
</p>
-<h2 id="CreateTestApp">Creating a Test Application</h2>
+<h2 id="CreateTestApp">Creating a Test Package</h2>
<p>
- Once you have created a test project, you populate it with a test application.
+ Once you have created a test project, you populate it with a test package.
The application does not require an {@link android.app.Activity Activity},
- although you can define one if you wish. Although your test application can
+ although you can define one if you wish. Although your test package can
combine Activities, Android test class extensions, JUnit extensions, or
ordinary classes, you should extend one of the Android test classes or JUnit classes,
because these provide the best testing features.
@@ -248,7 +253,7 @@
</p>
<p>
- To create a test application, start with one of Android's test classes in the Java package
+ To create a test package, start with one of Android's test classes in the Java package
{@link android.test android.test}. These extend the JUnit
{@link junit.framework.TestCase TestCase} class. With a few exceptions, the Android test
classes also provide instrumentation for testing.
@@ -282,24 +287,17 @@
test results are suspect, regardless of whether or not the tests succeeded.
</p>
<p>
- To learn more about creating test applications, see the topic <a
- href="{@docRoot}guide/topics/testing/testing_android.html">Testing and Instrumentation</a>,
+ To learn more about creating test packages, see the topic <a
+ href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
which provides an overview of Android testing. If you prefer to follow a tutorial,
try the <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
tutorial, which leads you through the creation of tests for an actual Android application.
</p>
<h2 id="RunTestsCommand">Running Tests</h2>
<p>
- If you are not developing in Eclipse with ADT, you need to run tests from the command line.
- You can do this either with Ant or with the {@link android.app.ActivityManager ActivityManager}
- command line interface.
-</p>
-<p>
- You can also run tests from the command line even if you are using Eclipse with ADT to develop
- them. To do this, you need to create the proper files and directory structure in the test
- project, using the <code>android</code> tool with the option <code>create test-project</code>.
- This is described in the section <a
- href="#CreateTestProjectCommand">Working with Test Projects</a>.
+ You run tests from the command line, either with Ant or with an
+ <a href="{@docRoot}http://developer.android.com/guide/developing/tools/adb.html">
+ Android Debug Bridge (adb)</a> shell.
</p>
<h3 id="RunTestsAnt">Quick build and run with Ant</h3>
<p>
@@ -316,57 +314,63 @@
You can update an existing test project to use this feature. To do this, use the
<code>android</code> tool with the <code>update test-project</code> option. This is described
in the section <a href="#UpdateTestProject">Updating a test project</a>.
+</p>
<h3 id="RunTestsDevice">Running tests on a device or emulator</h3>
<p>
- When you run tests from the command line with the ActivityManager (<code>am</code>)
- command-line tool, you get more options for choosing the tests to run than with any other
- method. You can select individual test methods, filter tests according to their annotation, or
- specify testing options. Since the test run is controlled entirely from a command line, you can
- customize your testing with shell scripts in various ways.
+ When you run tests from the command line with
+ <a href="{@docRoot}http://developer.android.com/guide/developing/tools/adb.html">
+ Android Debug Bridge (adb)</a>, you get more options for choosing the tests
+ to run than with any other method. You can select individual test methods, filter tests
+ according to their annotation, or specify testing options. Since the test run is controlled
+ entirely from a command line, you can customize your testing with shell scripts in various ways.
</p>
<p>
- You run the <code>am</code> tool on an Android device or emulator using the
- <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
- (<code>adb</code>) shell. When you do this, you use the ActivityManager
- <code>instrument</code> option to run your test application using an Android test runner
- (usually {@link android.test.InstrumentationTestRunner}). You set <code>am</code>
- options with command-line flags.
+ To run a test from the command line, you run <code>adb shell</code> to start a command-line
+ shell on your device or emulator, and then in the shell run the <code>am instrument</code>
+ command. You control <code>am</code> and your tests with command-line flags.
</p>
<p>
- To run a test with <code>am</code>:
+ As a shortcut, you can start an <code>adb</code> shell, call <code>am instrument</code>, and
+ specify command-line flags all on one input line. The shell opens on the device or emulator,
+ runs your tests, produces output, and then returns to the command line on your computer.
+</p>
+<p>
+ To run a test with <code>am instrument</code>:
</p>
<ol>
- <li>
- If necessary, re-build your main application and test application.
- </li>
- <li>
- Install your test application and main application Android package files
- (<code>.apk</code> files) to your current Android device or emulator</li>
- <li>
- At the command line, enter:
+ <li>
+ If necessary, rebuild your main application and test package.
+ </li>
+ <li>
+ Install your test package and main application Android package files
+ (<code>.apk</code> files) to your current Android device or emulator</li>
+ <li>
+ At the command line, enter:
<pre>
$ adb shell am instrument -w <test_package_name>/<runner_class>
</pre>
-<p>
- where <code><test_package_name></code> is the Android package name of your test
- application, and <code><runner_class></code> is the name of the Android test runner
- class you are using. The Android package name is the value of the <code>package</code>
- attribute of the <code>manifest</code> element in the manifest file
- (<code>AndroidManifest.xml</code>) of your test application. The Android test runner
- class is usually <code>InstrumentationTestRunner</code>.
-</p>
-<p>Your test results appear in <code>STDOUT</code>.</p>
- </li>
+ <p>
+ where <code><test_package_name></code> is the Android package name of your test
+ application, and <code><runner_class></code> is the name of the Android test
+ runner class you are using. The Android package name is the value of the
+ <code>package</code> attribute of the <code>manifest</code> element in the manifest file
+ (<code>AndroidManifest.xml</code>) of your test package. The Android test runner
+ class is usually {@link android.test.InstrumentationTestRunner}.
+ </p>
+ <p>
+ Your test results appear in <code>STDOUT</code>.
+ </p>
+ </li>
</ol>
<p>
- This operation starts an <code>adb</code> shell, then runs <code>am instrument</code> in it
+ This operation starts an <code>adb</code> shell, then runs <code>am instrument</code>
with the specified parameters. This particular form of the command will run all of the tests
- in your test application. You can control this behavior with flags that you pass to
+ in your test package. You can control this behavior with flags that you pass to
<code>am instrument</code>. These flags are described in the next section.
</p>
-<h2 id="AMSyntax">Using the Instrument Command</h2>
+<h2 id="AMSyntax">Using the am instrument Command</h2>
<p>
- The general syntax of the <code>am instrument</code> command is:
+ The general syntax of the <code>am instrument</code> command is:
</p>
<pre>
am instrument [flags] <test_package>/<runner_class>
@@ -391,11 +395,11 @@
<code><test_package></code>
</td>
<td>
- The Android package name of the test application.
+ The Android package name of the test package.
</td>
<td>
The value of the <code>package</code> attribute of the <code>manifest</code>
- element in the test application's manifest file.
+ element in the test package's manifest file.
</td>
</tr>
<tr>
@@ -411,7 +415,7 @@
</tr>
</table>
<p>
-The flags for <code>am instrument</code> are described in the following table:
+ The flags for <code>am instrument</code> are described in the following table:
</p>
<table>
<tr>
@@ -461,20 +465,21 @@
<test_options>
</td>
<td>
- Provides testing options , in the form of key-value pairs. The
+ Provides testing options as key-value pairs. The
<code>am instrument</code> tool passes these to the specified instrumentation class
via its <code>onCreate()</code> method. You can specify multiple occurrences of
- <code>-e <test_options</code>. The keys and values are described in the next table.
+ <code>-e <test_options></code>. The keys and values are described in the
+ section <a href="#AMOptionsSyntax">am instrument options</a>.
<p>
- The only instrumentation class that understands these key-value pairs is
- <code>InstrumentationTestRunner</code> (or a subclass). Using them with
+ The only instrumentation class that uses these key-value pairs is
+ {@link android.test.InstrumentationTestRunner} (or a subclass). Using them with
any other class has no effect.
</p>
</td>
</tr>
</table>
-<h3 id="AMOptionsSyntax">Instrument options</h3>
+<h3 id="AMOptionsSyntax">am instrument options</h3>
<p>
The <code>am instrument</code> tool passes testing options to
<code>InstrumentationTestRunner</code> or a subclass in the form of key-value pairs,
@@ -484,123 +489,127 @@
-e <key> <value>
</pre>
<p>
- Where applicable, a <key> may have multiple values separated by a comma (,).
+ Some keys accept multiple values. You specify multiple values in a comma-separated list.
For example, this invocation of <code>InstrumentationTestRunner</code> provides multiple
values for the <code>package</code> key:
+</p>
<pre>
-$ adb shell am instrument -w -e package com.android.test.package1,com.android.test.package2 com.android.test/android.test.InstrumentationTestRunner
+$ adb shell am instrument -w -e package com.android.test.package1,com.android.test.package2 \
+> com.android.test/android.test.InstrumentationTestRunner
</pre>
<p>
The following table describes the key-value pairs and their result. Please review the
<strong>Usage Notes</strong> following the table.
</p>
<table>
-<tr>
- <th>Key</th>
- <th>Value</th>
- <th>Description</th>
-</tr>
-<tr>
- <td>
- <code>package</code>
- </td>
- <td>
- <Java_package_name>
- </td>
- <td>
- The fully-qualified <em>Java</em> package name for one of the packages in the test
- application. Any test case class that uses this package name is executed. Notice that this
- is not an <em>Android</em> package name; a test application has a single Android package
- name but may have several Java packages within it.
- </td>
-</tr>
-<tr>
- <td rowspan="2"><code>class</code></td>
- <td><class_name></td>
- <td>
- The fully-qualified Java class name for one of the test case classes. Only this test case
- class is executed.
- </td>
-</tr>
-<tr>
- <td><class_name><strong>#</strong>method name</td>
- <td>
- A fully-qualified test case class name, and one of its methods. Only this method is
- executed. Note the hash mark (#) between the class name and the method name.
- </td>
-</tr>
-<tr>
- <td><code>func</code></td>
- <td><code>true</code></td>
- <td>
- Runs all test classes that extend {@link android.test.InstrumentationTestCase}.
- </td>
-</tr>
-<tr>
- <td><code>unit</code></td>
- <td><code>true</code></td>
- <td>
- Runs all test classes that do <em>not</em> extend either
- {@link android.test.InstrumentationTestCase} or {@link android.test.PerformanceTestCase}.
- </td>
-</tr>
-<tr>
- <td><code>size</code></td>
- <td>[<code>small</code> | <code>medium</code> | <code>large</code>]
- </td>
- <td>
- Runs a test method annotated by size. The annotations are <code>@SmallTest</code>,
- <code>@MediumTest</code>, and <code>@LargeTest</code>.
- </td>
-</tr>
-<tr>
- <td><code>perf</code></td>
- <td><code>true</code></td>
- <td>
- Runs all test classes that implement {@link android.test.PerformanceTestCase}.
- When you use this option, also specify the <code>-r</code> flag for
- <code>am instrument</code>, so that the output is kept in raw format and not
- re-formatted as test results.
- </td>
-</tr>
-<tr>
- <td><code>debug</code></td>
- <td><code>true</code></td>
- <td>
- Runs tests in debug mode.
- </td>
-</tr>
-<tr>
- <td><code>log</code></td>
- <td><code>true</code></td>
- <td>
- Loads and logs all specified tests, but does not run them. The test
- information appears in <code>STDOUT</code>. Use this to verify combinations of other filters
- and test specifications.
- </td>
-</tr>
-<tr>
- <td><code>emma</code></td>
- <td><code>true</code></td>
- <td>
- Runs an EMMA code coverage analysis and writes the output to <code>/data//coverage.ec</code>
- on the device. To override the file location, use the <code>coverageFile</code> key that
- is described in the following entry.
- <p class="note">
- <strong>Note:</strong> This option requires an EMMA-instrumented build of the test
- application, which you can generate with the <code>coverage</code> target.
- </p>
- </td>
-</tr>
-<tr>
- <td><code>coverageFile</code></td>
- <td><code><filename></code></td>
- <td>
- Overrides the default location of the EMMA coverage file on the device. Specify this
- value as a path and filename in UNIX format. The default filename is described in the
- entry for the <code>emma</code> key.
- </td>
-</tr>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td>
+ <code>package</code>
+ </td>
+ <td>
+ <Java_package_name>
+ </td>
+ <td>
+ The fully-qualified <em>Java</em> package name for one of the packages in the test
+ application. Any test case class that uses this package name is executed. Notice that
+ this is not an <em>Android</em> package name; a test package has a single
+ Android package name but may have several Java packages within it.
+ </td>
+ </tr>
+ <tr>
+ <td rowspan="2"><code>class</code></td>
+ <td><class_name></td>
+ <td>
+ The fully-qualified Java class name for one of the test case classes. Only this test
+ case class is executed.
+ </td>
+ </tr>
+ <tr>
+ <td><class_name><strong>#</strong>method name</td>
+ <td>
+ A fully-qualified test case class name, and one of its methods. Only this method is
+ executed. Note the hash mark (#) between the class name and the method name.
+ </td>
+ </tr>
+ <tr>
+ <td><code>func</code></td>
+ <td><code>true</code></td>
+ <td>
+ Runs all test classes that extend {@link android.test.InstrumentationTestCase}.
+ </td>
+ </tr>
+ <tr>
+ <td><code>unit</code></td>
+ <td><code>true</code></td>
+ <td>
+ Runs all test classes that do <em>not</em> extend either
+ {@link android.test.InstrumentationTestCase} or
+ {@link android.test.PerformanceTestCase}.
+ </td>
+ </tr>
+ <tr>
+ <td><code>size</code></td>
+ <td>
+ [<code>small</code> | <code>medium</code> | <code>large</code>]
+ </td>
+ <td>
+ Runs a test method annotated by size. The annotations are <code>@SmallTest</code>,
+ <code>@MediumTest</code>, and <code>@LargeTest</code>.
+ </td>
+ </tr>
+ <tr>
+ <td><code>perf</code></td>
+ <td><code>true</code></td>
+ <td>
+ Runs all test classes that implement {@link android.test.PerformanceTestCase}.
+ When you use this option, also specify the <code>-r</code> flag for
+ <code>am instrument</code>, so that the output is kept in raw format and not
+ re-formatted as test results.
+ </td>
+ </tr>
+ <tr>
+ <td><code>debug</code></td>
+ <td><code>true</code></td>
+ <td>
+ Runs tests in debug mode.
+ </td>
+ </tr>
+ <tr>
+ <td><code>log</code></td>
+ <td><code>true</code></td>
+ <td>
+ Loads and logs all specified tests, but does not run them. The test
+ information appears in <code>STDOUT</code>. Use this to verify combinations of other
+ filters and test specifications.
+ </td>
+ </tr>
+ <tr>
+ <td><code>emma</code></td>
+ <td><code>true</code></td>
+ <td>
+ Runs an EMMA code coverage analysis and writes the output to
+ <code>/data//coverage.ec</code> on the device. To override the file location, use the
+ <code>coverageFile</code> key that is described in the following entry.
+ <p class="note">
+ <strong>Note:</strong> This option requires an EMMA-instrumented build of the test
+ application, which you can generate with the <code>coverage</code> target.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>coverageFile</code></td>
+ <td><code><filename></code></td>
+ <td>
+ Overrides the default location of the EMMA coverage file on the device. Specify this
+ value as a path and filename in UNIX format. The default filename is described in the
+ entry for the <code>emma</code> key.
+ </td>
+ </tr>
</table>
<strong><code>-e</code> Flag Usage Notes</strong>
<ul>
@@ -618,13 +627,13 @@
The <code>func</code> key and <code>unit</code> key are mutually exclusive.
</li>
</ul>
-<h3 id="RunTestExamples">Instrument examples</h3>
+<h3 id="RunTestExamples">Usage examples</h3>
<p>
-Here are some examples of using <code>am instrument</code> to run tests. They are based on
-the following structure:</p>
+The following sections provide examples of using <code>am instrument</code> to run tests.
+They are based on the following structure:</p>
<ul>
<li>
- The test application has the Android package name <code>com.android.demo.app.tests</code>
+ The test package has the Android package name <code>com.android.demo.app.tests</code>
</li>
<li>
There are three test classes:
@@ -647,35 +656,35 @@
The test runner is {@link android.test.InstrumentationTestRunner}.
</li>
</ul>
-<h4>Running the Entire Test Application</h4>
+<h4>Running the entire test package</h4>
<p>
- To run all of the test classes in the test application, enter:
+ To run all of the test classes in the test package, enter:
</p>
<pre>
$ adb shell am instrument -w com.android.demo.app.tests/android.test.InstrumentationTestRunner
</pre>
-<h4>Running All Tests in a Test Case Class</h4>
+<h4>Running all tests in a test case class</h4>
<p>
To run all of the tests in the class <code>UnitTests</code>, enter:
</p>
<pre>
$ adb shell am instrument -w \
--e class com.android.demo.app.tests.UnitTests \
-com.android.demo.app.tests/android.test.InstrumentationTestRunner
+> -e class com.android.demo.app.tests.UnitTests \
+> com.android.demo.app.tests/android.test.InstrumentationTestRunner
</pre>
<p>
<code>am instrument</code> gets the value of the <code>-e</code> flag, detects the
<code>class</code> keyword, and runs all the methods in the <code>UnitTests</code> class.
</p>
-<h4>Selecting a Subset of Tests</h4>
+<h4>Selecting a subset of tests</h4>
<p>
- To run all of the tests in <code>UnitTests</code>, and the <code>testCamera</code> method in
- <code>FunctionTests</code>, enter:
+ To run all of the tests in <code>UnitTests</code>, and the <code>testCamera</code> method in
+ <code>FunctionTests</code>, enter:
</p>
<pre>
$ adb shell am instrument -w \
--e class com.android.demo.app.tests.UnitTests,com.android.demo.app.tests.FunctionTests#testCamera \
-com.android.demo.app.tests/android.test.InstrumentationTestRunner
+> -e class com.android.demo.app.tests.UnitTests,com.android.demo.app.tests.FunctionTests#testCamera \
+> com.android.demo.app.tests/android.test.InstrumentationTestRunner
</pre>
<p>
You can find more examples of the command in the documentation for
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index cdf5feb..2b803424 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -254,12 +254,34 @@
<li><a href="<?cs var:toroot?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li>
</ul>
</li>
- <li><a href="<?cs var:toroot?>guide/topics/testing/testing_android.html">
- <span class="en">Testing and Instrumentation</span>
- </a></li>
+ <li class="toggle-list">
+ <div>
+ <a href="<?cs var:toroot ?>guide/topics/testing/index.html">
+ <span class="en">Testing</span>
+ </a> <span class="new">new!</span>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot?>guide/topics/testing/testing_android.html">
+ <span class="en">Testing Fundamentals</span></a>
+ </li>
+ <li><a href="<?cs var:toroot?>guide/topics/testing/activity_testing.html">
+ <span class="en">Activity Testing</span></a>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/testing/contentprovider_testing.html">
+ <span class="en">Content Provider Testing</span></a>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/testing/service_testing.html">
+ <span class="en">Service Testing</span></a>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/testing/what_to_test.html">
+ <span class="en">What To Test</span></a>
+ </li>
+ </ul>
+ </li>
<li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html">
<span class="en">Device Administration</span>
- </a> <span class="new">new!</span><!-- 10/8/10 --></li>
+ </a> <span class="new">new!</span>
+ </li>
</ul>
</li>
diff --git a/docs/html/guide/practices/design/responsiveness.jd b/docs/html/guide/practices/design/responsiveness.jd
index b811d1b..a00e3aa 100644
--- a/docs/html/guide/practices/design/responsiveness.jd
+++ b/docs/html/guide/practices/design/responsiveness.jd
@@ -99,6 +99,10 @@
event timeout. These same practices should be followed for any other threads
that display UI, as they are also subject to the same timeouts.</p>
+<p>You can use {@link android.os.StrictMode} to help find potentially
+long running operations such as network or database operations that
+you might accidentally be doing your main thread.</p>
+
<p>The specific constraint on IntentReceiver execution time emphasizes what
they were meant to do: small, discrete amounts of work in the background such
as saving a setting or registering a Notification. So as with other methods
diff --git a/docs/html/guide/topics/testing/activity_testing.jd b/docs/html/guide/topics/testing/activity_testing.jd
new file mode 100644
index 0000000..6392ad7
--- /dev/null
+++ b/docs/html/guide/topics/testing/activity_testing.jd
@@ -0,0 +1,392 @@
+page.title=Activity Testing
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li>
+ <a href="#ActivityTestAPI">The Activity Testing API</a>
+ <ol>
+ <li>
+ <a href="#ActivityInstrumentationTestCase2">ActivityInstrumentationTestCase2</a>
+ </li>
+ <li>
+ <a href="#ActivityUnitTestCase">ActivityUnitTestCase</a>
+ </li>
+ <li>
+ <a href="#SingleLaunchActivityTestCase">SingleLaunchActivityTestCase</a>
+ </li>
+ <li>
+ <a href="#MockObjectNotes">Mock objects and activity testing</a>
+ </li>
+ <li>
+ <a href="#AssertionNotes">Assertions for activity testing</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#WhatToTest">What to Test</a>
+ </li>
+ <li>
+ <a href="#NextSteps">Next Steps</a>
+ </li>
+ <li>
+ <a href="#UITesting">Appendix: UI Testing Notes</a>
+ <ol>
+ <li>
+ <a href="#RunOnUIThread">Testing on the UI thread</a>
+ </li>
+ <li>
+ <a href="#NotouchMode">Turning off touch mode</a>
+ </li>
+ <li>
+ <a href="#UnlockDevice">Unlocking the Emulator or Device</a>
+ </li>
+ <li>
+ <a href="#UITestTroubleshooting">Troubleshooting UI tests</a>
+ </li>
+ </ol>
+ </li>
+ </ol>
+<h2>Key Classes</h2>
+ <ol>
+ <li>{@link android.test.InstrumentationTestRunner}</li>
+ <li>{@link android.test.ActivityInstrumentationTestCase2}</li>
+ <li>{@link android.test.ActivityUnitTestCase}</li>
+ </ol>
+<h2>Related Tutorials</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">
+ Hello, Testing</a>
+ </li>
+ <li>
+ <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
+ </li>
+ </ol>
+<h2>See Also</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in Other IDEs</a>
+ </li>
+ </ol>
+ </div>
+</div>
+<p>
+ Activity testing is particularly dependent on the the Android instrumentation framework.
+ Unlike other components, activities have a complex lifecycle based on callback methods; these
+ can't be invoked directly except by instrumentation. Also, the only way to send events to the
+ user interface from a program is through instrumentation.
+</p>
+<p>
+ This document describes how to test activities using instrumentation and other test
+ facilities. The document assumes you have already read
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
+ the introduction to the Android testing and instrumentation framework.
+</p>
+<h2 id="ActivityTestAPI">The Activity Testing API</h2>
+<p>
+ The activity testing API base class is {@link android.test.InstrumentationTestCase},
+ which provides instrumentation to the test case subclasses you use for Activities.
+</p>
+<p>
+ For activity testing, this base class provides these functions:
+</p>
+<ul>
+ <li>
+ Lifecycle control: With instrumentation, you can start the activity under test, pause it,
+ and destroy it, using methods provided by the test case classes.
+ </li>
+ <li>
+ Dependency injection: Instrumentation allows you to create mock system objects such as
+ Contexts or Applications and use them to run the activity under test. This
+ helps you control the test environment and isolate it from production systems. You can
+ also set up customized Intents and start an activity with them.
+ </li>
+ <li>
+ User interface interaction: You use instrumentation to send keystrokes or touch events
+ directly to the UI of the activity under test.
+ </li>
+</ul>
+<p>
+ The activity testing classes also provide the JUnit framework by extending
+ {@link junit.framework.TestCase} and {@link junit.framework.Assert}.
+</p>
+<p>
+ The two main testing subclasses are {@link android.test.ActivityInstrumentationTestCase2} and
+ {@link android.test.ActivityUnitTestCase}. To test an Activity that is launched in a mode
+ other than <code>standard</code>, you use {@link android.test.SingleLaunchActivityTestCase}.
+</p>
+<h3 id="ActivityInstrumentationTestCase2">ActivityInstrumentationTestCase2</h3>
+<p>
+ The {@link android.test.ActivityInstrumentationTestCase2} test case class is designed to do
+ functional testing of one or more Activities in an application, using a normal system
+ infrastructure. It runs the Activities in a normal instance of the application under test,
+ using a standard system Context. It allows you to send mock Intents to the activity under
+ test, so you can use it to test an activity that responds to multiple types of intents, or
+ an activity that expects a certain type of data in the intent, or both. Notice, though, that it
+ does not allow mock Contexts or Applications, so you can not isolate the test from the rest of
+ a production system.
+</p>
+<h3 id="ActivityUnitTestCase">ActivityUnitTestCase</h3>
+<p>
+ The {@link android.test.ActivityUnitTestCase} test case class tests a single activity in
+ isolation. Before you start the activity, you can inject a mock Context or Application, or both.
+ You use it to run activity tests in isolation, and to do unit testing of methods
+ that do not interact with Android. You can not send mock Intents to the activity under test,
+ although you can call
+ {@link android.app.Activity#startActivity(Intent) Activity.startActivity(Intent)} and then
+ look at arguments that were received.
+</p>
+<h3 id="SingleLaunchActivityTestCase">SingleLaunchActivityTestCase</h3>
+<p>
+ The {@link android.test.SingleLaunchActivityTestCase} class is a convenience class for
+ testing a single activity in an environment that doesn't change from test to test.
+ It invokes {@link junit.framework.TestCase#setUp() setUp()} and
+ {@link junit.framework.TestCase#tearDown() tearDown()} only once, instead of once per
+ method call. It does not allow you to inject any mock objects.
+</p>
+<p>
+ This test case is useful for testing an activity that runs in a mode other than
+ <code>standard</code>. It ensures that the test fixture is not reset between tests. You
+ can then test that the activity handles multiple calls correctly.
+</p>
+<h3 id="MockObjectNotes">Mock objects and activity testing</h3>
+<p>
+ This section contains notes about the use of the mock objects defined in
+ {@link android.test.mock} with activity tests.
+</p>
+<p>
+ The mock object {@link android.test.mock.MockApplication} is only available for activity
+ testing if you use the {@link android.test.ActivityUnitTestCase} test case class.
+ By default, <code>ActivityUnitTestCase</code>, creates a hidden <code>MockApplication</code>
+ object that is used as the application under test. You can inject your own object using
+ {@link android.test.ActivityUnitTestCase#setApplication(Application) setApplication()}.
+</p>
+<h3 id="AssertionNotes">Assertions for activity testing</h3>
+<p>
+ {@link android.test.ViewAsserts} defines assertions for Views. You use it to verify the
+ alignment and position of View objects, and to look at the state of ViewGroup objects.
+</p>
+<h2 id="WhatToTest">What To Test</h2>
+<ul>
+ <li>
+ Input validation: Test that an activity responds correctly to input values in an
+ EditText View. Set up a keystroke sequence, send it to the activity, and then
+ use {@link android.view.View#findViewById(int)} to examine the state of the View. You can
+ verify that a valid keystroke sequence enables an OK button, while an invalid one leaves the
+ button disabled. You can also verify that the Activity responds to invalid input by
+ setting error messages in the View.
+ </li>
+ <li>
+ Lifecycle events: Test that each of your application's activities handles lifecycle events
+ correctly. In general, lifecycle events are actions, either from the system or from the
+ user, that trigger a callback method such as <code>onCreate()</code> or
+ <code>onClick()</code>. For example, an activity should respond to pause or destroy events
+ by saving its state. Remember that even a change in screen orientation causes the current
+ activity to be destroyed, so you should test that accidental device movements don't
+ accidentally lose the application state.
+ </li>
+ <li>
+ Intents: Test that each activity correctly handles the intents listed in the intent
+ filter specified in its manifest. You can use
+ {@link android.test.ActivityInstrumentationTestCase2} to send mock Intents to the
+ activity under test.
+ </li>
+ <li>
+ Runtime configuration changes: Test that each activity responds correctly to the
+ possible changes in the device's configuration while your application is running. These
+ include a change to the device's orientation, a change to the current language, and so
+ forth. Handling these changes is described in detail in the topic
+ <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime
+ Changes</a>.
+ </li>
+ <li>
+ Screen sizes and resolutions: Before you publish your application, make sure to test it on
+ all of the screen sizes and densities on which you want it to run. You can test the
+ application on multiple sizes and densities using AVDs, or you can test your application
+ directly on the devices that you are targeting. For more information, see the topic
+ <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.
+ </li>
+</ul>
+<h2 id="NextSteps">Next Steps</h2>
+<p>
+ To learn how to set up and run tests in Eclipse, please refer to <a
+ href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in
+ Eclipse, with ADT</a>. If you're not working in Eclipse, refer to <a
+ href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other
+ IDEs</a>.
+</p>
+<p>
+ If you want a step-by-step introduction to testing activities, try one of the
+ testing tutorials:
+</p>
+<ul>
+ <li>
+ The <a
+ href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">Hello,
+ Testing</a> tutorial introduces basic testing concepts and procedures in the
+ context of the Hello, World application.
+ </li>
+ <li>
+ The <a
+ href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity
+ Testing</a> tutorial is an excellent follow-up to the Hello, Testing tutorial.
+ It guides you through a more complex testing scenario that you develop against a
+ more realistic activity-oriented application.
+ </li>
+</ul>
+<h2 id="UITesting">Appendix: UI Testing Notes</h2>
+<p>
+ The following sections have tips for testing the UI of your Android application, specifically
+ to help you handle actions that run in the UI thread, touch screen and keyboard events, and home
+ screen unlock during testing.
+</p>
+<h3 id="RunOnUIThread">Testing on the UI thread</h3>
+<p>
+ An application's activities run on the application's <strong>UI thread</strong>. Once the
+ UI is instantiated, for example in the activity's <code>onCreate()</code> method, then all
+ interactions with the UI must run in the UI thread. When you run the application normally, it
+ has access to the thread and does not have to do anything special.
+</p>
+<p>
+ This changes when you run tests against the application. With instrumentation-based classes,
+ you can invoke methods against the UI of the application under test. The other test classes
+ don't allow this. To run an entire test method on the UI thread, you can annotate the thread
+ with <code>@UIThreadTest</code>. Notice that this will run <em>all</em> of the method statements
+ on the UI thread. Methods that do not interact with the UI are not allowed; for example, you
+ can't invoke <code>Instrumentation.waitForIdleSync()</code>.
+</p>
+<p>
+ To run a subset of a test method on the UI thread, create an anonymous class of type
+ <code>Runnable</code>, put the statements you want in the <code>run()</code> method, and
+ instantiate a new instance of the class as a parameter to the method
+ <code><em>appActivity</em>.runOnUiThread()</code>, where <code><em>appActivity</em></code> is
+ the instance of the application you are testing.
+</p>
+<p>
+ For example, this code instantiates an activity to test, requests focus (a UI action) for the
+ Spinner displayed by the activity, and then sends a key to it. Notice that the calls to
+ <code>waitForIdleSync</code> and <code>sendKeys</code> aren't allowed to run on the UI thread:
+</p>
+<pre>
+ private MyActivity mActivity; // MyActivity is the class name of the app under test
+ private Spinner mSpinner;
+
+ ...
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+
+ mActivity = getActivity(); // get a references to the app under test
+
+ /*
+ * Get a reference to the main widget of the app under test, a Spinner
+ */
+ mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);
+
+ ...
+
+ public void aTest() {
+ /*
+ * request focus for the Spinner, so that the test can send key events to it
+ * This request must be run on the UI thread. To do this, use the runOnUiThread method
+ * and pass it a Runnable that contains a call to requestFocus on the Spinner.
+ */
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ mSpinner.requestFocus();
+ }
+ });
+
+ mInstrumentation.waitForIdleSync();
+
+ this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+</pre>
+
+<h3 id="NotouchMode">Turning off touch mode</h3>
+<p>
+ To control the emulator or a device with key events you send from your tests, you must turn off
+ touch mode. If you do not do this, the key events are ignored.
+</p>
+<p>
+ To turn off touch mode, you invoke
+ <code>ActivityInstrumentationTestCase2.setActivityTouchMode(false)</code>
+ <em>before</em> you call <code>getActivity()</code> to start the activity. You must invoke the
+ method in a test method that is <em>not</em> running on the UI thread. For this reason, you
+ can't invoke the touch mode method from a test method that is annotated with
+ <code>@UIThread</code>. Instead, invoke the touch mode method from <code>setUp()</code>.
+</p>
+<h3 id="UnlockDevice">Unlocking the emulator or device</h3>
+<p>
+ You may find that UI tests don't work if the emulator's or device's home screen is disabled with
+ the keyguard pattern. This is because the application under test can't receive key events sent
+ by <code>sendKeys()</code>. The best way to avoid this is to start your emulator or device
+ first and then disable the keyguard for the home screen.
+</p>
+<p>
+ You can also explicitly disable the keyguard. To do this,
+ you need to add a permission in the manifest file (<code>AndroidManifest.xml</code>) and
+ then disable the keyguard in your application under test. Note, though, that you either have to
+ remove this before you publish your application, or you have to disable it with code in
+ the published application.
+</p>
+<p>
+ To add the the permission, add the element
+ <code><uses-permission android:name="android.permission.DISABLE_KEYGUARD"/></code>
+ as a child of the <code><manifest></code> element. To disable the KeyGuard, add the
+ following code to the <code>onCreate()</code> method of activities you intend to test:
+</p>
+<pre>
+ mKeyGuardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+ mLock = mKeyGuardManager.newKeyguardLock("<em>activity_classname</em>");
+ mLock.disableKeyguard();
+</pre>
+<p>where <code><em>activity_classname</em></code> is the class name of the activity.</p>
+<h3 id="UITestTroubleshooting">Troubleshooting UI tests</h3>
+<p>
+ This section lists some of the common test failures you may encounter in UI testing, and their
+ causes:
+</p>
+<dl>
+ <dt><code>WrongThreadException</code></dt>
+ <dd>
+ <p><strong>Problem:</strong></p>
+ For a failed test, the Failure Trace contains the following error message:
+ <code>
+ android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created
+ a view hierarchy can touch its views.
+ </code>
+ <p><strong>Probable Cause:</strong></p>
+ This error is common if you tried to send UI events to the UI thread from outside the UI
+ thread. This commonly happens if you send UI events from the test application, but you don't
+ use the <code>@UIThread</code> annotation or the <code>runOnUiThread()</code> method. The
+ test method tried to interact with the UI outside the UI thread.
+ <p><strong>Suggested Resolution:</strong></p>
+ Run the interaction on the UI thread. Use a test class that provides instrumentation. See
+ the previous section <a href="#RunOnUIThread">Testing on the UI Thread</a>
+ for more details.
+ </dd>
+ <dt><code>java.lang.RuntimeException</code></dt>
+ <dd>
+ <p><strong>Problem:</strong></p>
+ For a failed test, the Failure Trace contains the following error message:
+ <code>
+ java.lang.RuntimeException: This method can not be called from the main application thread
+ </code>
+ <p><strong>Probable Cause:</strong></p>
+ This error is common if your test method is annotated with <code>@UiThreadTest</code> but
+ then tries to do something outside the UI thread or tries to invoke
+ <code>runOnUiThread()</code>.
+ <p><strong>Suggested Resolution:</strong></p>
+ Remove the <code>@UiThreadTest</code> annotation, remove the <code>runOnUiThread()</code>
+ call, or re-factor your tests.
+ </dd>
+</dl>
diff --git a/docs/html/guide/topics/testing/contentprovider_testing.jd b/docs/html/guide/topics/testing/contentprovider_testing.jd
new file mode 100644
index 0000000..893b5c9
--- /dev/null
+++ b/docs/html/guide/topics/testing/contentprovider_testing.jd
@@ -0,0 +1,224 @@
+page.title=Content Provider Testing
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li>
+ <a href="#DesignAndTest">Content Provider Design and Testing</a>
+ </li>
+ <li>
+ <a href="#ContentProviderTestAPI">The Content Provider Testing API</a>
+ <ol>
+ <li>
+ <a href="#ProviderTestCase2">ProviderTestCase2 </a>
+ </li>
+ <li>
+ <a href="#MockObjects">Mock object classes</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#WhatToTest">What To Test</a>
+ </li>
+ <li>
+ <a href="#NextSteps">Next Steps</a>
+ </li>
+ </ol>
+ <h2>Key Classes</h2>
+ <ol>
+ <li>{@link android.test.InstrumentationTestRunner}</li>
+ <li>{@link android.test.ProviderTestCase2}</li>
+ <li>{@link android.test.IsolatedContext}</li>
+ <li>{@link android.test.mock.MockContentResolver}</li>
+ </ol>
+ <h2>See Also</h2>
+ <ol>
+ <li>
+ <a
+ href="{@docRoot}guide/topics/testing/topics/testing_android.html">
+ Testing Fundamentals</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in Other IDEs</a>
+ </li>
+ </ol>
+ </div>
+</div>
+<p>
+ Content providers, which store and retrieve data and make it accessible across applications,
+ are a key part of the Android API. As an application developer you're allowed to provide your
+ own public providers for use by other applications. If you do, then you should test them
+ using the API you publish.
+</p>
+<p>
+ This document describes how to test public content providers, although the information is
+ also applicable to providers that you keep private to your own application. If you aren't
+ familiar with content providers or the Android testing framework, please read
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>,
+ the guide to developing content providers, and
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
+ the introduction to the Android testing and instrumentation framework.
+</p>
+<h2 id="DesignAndTest">Content Provider Design and Testing</h2>
+<p>
+ In Android, content providers are viewed externally as data APIs that provide
+ tables of data, with their internals hidden from view. A content provider may have many
+ public constants, but it usually has few if any public methods and no public variables.
+ This suggests that you should write your tests based only on the provider's public members.
+ A content provider that is designed like this is offering a contract between itself and its
+ users.
+</p>
+<p>
+ The base test case class for content providers,
+ {@link android.test.ProviderTestCase2}, allows you to test your content provider in an
+ isolated environment. Android mock objects such as {@link android.test.IsolatedContext} and
+ {@link android.test.mock.MockContentResolver} also help provide an isolated test environment.
+</p>
+<p>
+ As with other Android tests, provider test packages are run under the control of the test
+ runner {@link android.test.InstrumentationTestRunner}. The section
+ <a href="{@docRoot}guide/topics/testing/testing_android.html#InstrumentationTestRunner">
+ Running Tests With InstrumentationTestRunner</a> describes the test runner in
+ more detail. The topic <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a> shows you how to run a test package in Eclipse, and the
+ topic <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in Other IDEs</a>
+ shows you how to run a test package from the command line.
+</p>
+<h2 id="ContentProviderTestAPI">Content Provider Testing API</h2>
+<p>
+ The main focus of the provider testing API is to provide an isolated testing environment. This
+ ensures that tests always run against data dependencies set explicitly in the test case. It
+ also prevents tests from modifying actual user data. For example, you want to avoid writing
+ a test that fails because there was data left over from a previous test, and you want to
+ avoid adding or deleting contact information in a actual provider.
+</p>
+<p>
+ The test case class and mock object classes for provider testing set up this isolated testing
+ environment for you.
+</p>
+<h3 id="ProviderTestCase2">ProviderTestCase2</h3>
+<p>
+ You test a provider with a subclass of {@link android.test.ProviderTestCase2}. This base class
+ extends {@link android.test.AndroidTestCase}, so it provides the JUnit testing framework as well
+ as Android-specific methods for testing application permissions. The most important
+ feature of this class is its initialization, which creates the isolated test environment.
+</p>
+<p>
+ The initialization is done in the constructor for {@link android.test.ProviderTestCase2}, which
+ subclasses call in their own constructors. The {@link android.test.ProviderTestCase2}
+ constructor creates an {@link android.test.IsolatedContext} object that allows file and
+ database operations but stubs out other interactions with the Android system.
+ The file and database operations themselves take place in a directory that is local to the
+ device or emulator and has a special prefix.
+</p>
+<p>
+ The constructor then creates a {@link android.test.mock.MockContentResolver} to use as the
+ resolver for the test. The {@link android.test.mock.MockContentResolver} class is described in
+ detail in the section
+ <a href="{@docRoot}guide/topics/testing/test_android#MockObjectClasses">Mock object classes</a>.
+</p>
+<p>
+ Lastly, the constructor creates an instance of the provider under test. This is a normal
+ {@link android.content.ContentProvider} object, but it takes all of its environment information
+ from the {@link android.test.IsolatedContext}, so it is restricted to
+ working in the isolated test environment. All of the tests done in the test case class run
+ against this isolated object.
+</p>
+<h3 id="MockObjects">Mock object classes</h3>
+<p>
+ {@link android.test.ProviderTestCase2} uses {@link android.test.IsolatedContext} and
+ {@link android.test.mock.MockContentResolver}, which are standard mock object classes. To
+ learn more about them, please read
+ <a href="{@docRoot}guide/topics/testing/test_android#MockObjectClasses">
+ Testing Fundamentals</a>.
+</p>
+<h2 id="WhatToTest">What To Test</h2>
+<p>
+ The topic <a href="{@docRoot}guide/topics/testing/what_to_test.html">What To Test</a>
+ lists general considerations for testing Android components.
+ Here are some specific guidelines for testing content providers.
+</p>
+<ul>
+ <li>
+ Test with resolver methods: Even though you can instantiate a provider object in
+ {@link android.test.ProviderTestCase2}, you should always test with a resolver object
+ using the appropriate URI. This ensures that you are testing the provider using the same
+ interaction that a regular application would use.
+ </li>
+ <li>
+ Test a public provider as a contract: If you intent your provider to be public and
+ available to other applications, you should test it as a contract. This includes
+ the following ideas:
+ <ul>
+ <li>
+ Test with constants that your provider publicly exposes. For
+ example, look for constants that refer to column names in one of the provider's
+ data tables. These should always be constants publicly defined by the provider.
+ </li>
+ <li>
+ Test all the URIs offered by your provider. Your provider may offer several URIs,
+ each one referring to a different aspect of the data. The
+ <a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> sample,
+ for example, features a provider that offers one URI for retrieving a list of notes,
+ another for retrieving an individual note by it's database ID, and a third for
+ displaying notes in a live folder. The sample test package for Note Pad,
+ <a href="{@docRoot}resources/samples/NotePadTest/index.html"> Note Pad Test</a>, has
+ unit tests for two of these URIs.
+ </li>
+ <li>
+ Test invalid URIs: Your unit tests should deliberately call the provider with an
+ invalid URI, and look for errors. Good provider design is to throw an
+ IllegalArgumentException for invalid URIs.
+
+ </li>
+ </ul>
+ </li>
+ <li>
+ Test the standard provider interactions: Most providers offer six access methods:
+ query, insert, delete, update, getType, and onCreate(). Your tests should verify that all
+ of these methods work. These are described in more detail in the topic
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>.
+ </li>
+ <li>
+ Test business logic: Don't forget to test the business logic that your provider should
+ enforce. Business logic includes handling of invalid values, financial or arithmetic
+ calculations, elimination or combining of duplicates, and so forth. A content provider
+ does not have to have business logic, because it may be implemented by activities that
+ modify the data. If the provider does implement business logic, you should test it.
+ </li>
+</ul>
+<h2 id="NextSteps">Next Steps</h2>
+<p>
+ To learn how to set up and run tests in Eclipse, please refer to <a
+ href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in
+ Eclipse, with ADT</a>. If you're not working in Eclipse, refer to <a
+ href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other
+ IDEs</a>.
+</p>
+<p>
+ If you want a step-by-step introduction to testing activities, try one of the
+ testing tutorials:
+</p>
+<ul>
+ <li>
+ The <a
+ href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">Hello,
+ Testing</a> tutorial introduces basic testing concepts and procedures in the
+ context of the Hello, World application.
+ </li>
+ <li>
+ The <a
+ href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity
+ Testing</a> tutorial is an excellent follow-up to the Hello, Testing tutorial.
+ It guides you through a more complex testing scenario that you develop against a
+ more realistic activity-oriented application.
+ </li>
+</ul>
diff --git a/docs/html/guide/topics/testing/index.jd b/docs/html/guide/topics/testing/index.jd
new file mode 100644
index 0000000..92ed5a7
--- /dev/null
+++ b/docs/html/guide/topics/testing/index.jd
@@ -0,0 +1,80 @@
+page.title=Testing
+@jd:body
+<p>
+ The Android development environment includes an integrated testing framework that helps you
+ test all aspects of your application.
+</p>
+<h4>Fundamentals</h4>
+<p>
+ To start learning how to use the framework to create tests for your applications, please
+ read the topic <a href="{@docRoot}guide/topics/testing/testing_android.html">
+ Testing Fundamentals</a>.
+</p>
+<h4>Concepts</h4>
+<ul>
+ <li>
+ Testing Tools describes the Eclipse with ADT and command-line tools you use to test
+ Android applications.
+ </li>
+ <li>
+ What to Test is an overview of the types of testing you should do. It focuses on testing
+ system-wide aspects of Android that can affect every component in your application.
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/activity_testing.html">
+ Activity Testing</a> focuses on testing activities. It describes how instrumentation allows
+ you to control activities outside the normal application lifecycle. It also lists
+ activity-specific features you should test, and it provides tips for testing Android
+ user interfaces.
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/contentprovider_testing.html">
+ Content Provider Testing</a> focuses on testing content providers. It describes the
+ mock system objects you can use, provides tips for designing providers so that they
+ can be tested, and lists provider-specific features you should test.
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/service_testing.html">
+ Service Testing</a> focuses on testing services. It also lists service-specific features
+ you should test.
+ </li>
+</ul>
+<h4>Procedures</h4>
+<ul>
+ <li>
+ The topic <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a> describes how to create and run tests in Eclipse with ADT.
+ </li>
+ <li>
+ The topic <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in other IDEs</a> describes how to create and run tests with command-line tools.
+ </li>
+</ul>
+<h4>Tutorials</h4>
+<ul>
+ <li>
+ The <a href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">
+ Hello, Testing</a> tutorial introduces basic testing concepts and procedures.
+ </li>
+ <li>
+ For a more advanced tutorial, try
+ <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>,
+ which guides you through a more complex testing scenario.
+ </li>
+</ul>
+<h4>Samples</h4>
+<ul>
+ <li>
+ <a href="{@docRoot}resources/samples/NotePadTest.html">Note Pad Provider
+ Test</a> is a test package for the
+ <a href="{@docRoot}resources/samples/NotePad.html">Note Pad</a> sample
+ application. It provides a simple example of unit testing
+ a {@link android.content.ContentProvider}.
+ </li>
+ <li>
+ The <a href="{@docRoot}resources/samples/AlarmServiceTest.html">Alarm Service Test</a>
+ is a test package for the <a href="{@docRoot}resources/samples/Alarm.html">Alarm</a>
+ sample application. It provides a simple example of unit
+ testing a {@link android.app.Service}.
+ </li>
+</ul>
diff --git a/docs/html/guide/topics/testing/service_testing.jd b/docs/html/guide/topics/testing/service_testing.jd
new file mode 100644
index 0000000..3979f3c
--- /dev/null
+++ b/docs/html/guide/topics/testing/service_testing.jd
@@ -0,0 +1,178 @@
+page.title=Service Testing
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li>
+ <a href="#DesignAndTest">Service Design and Testing</a>
+ </li>
+ <li>
+ <a href="#ServiceTestCase">ServiceTestCase</a>
+ </li>
+ <li>
+ <a href="#MockObjects">Mock object classes</a>
+ </li>
+ <li>
+ <a href="#TestAreas">What to Test</a>
+ </li>
+ </ol>
+ <h2>Key Classes</h2>
+ <ol>
+ <li>{@link android.test.InstrumentationTestRunner}</li>
+ <li>{@link android.test.ServiceTestCase}</li>
+ <li>{@link android.test.mock.MockApplication}</li>
+ <li>{@link android.test.RenamingDelegatingContext}</li>
+ </ol>
+ <h2>Related Tutorials</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">
+ Hello, Testing</a>
+ </li>
+ <li>
+ <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
+ </li>
+ </ol>
+ <h2>See Also</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in Other IDEs</a>
+ </li>
+ </ol>
+ </div>
+</div>
+<p>
+ Android provides a testing framework for Service objects that can run them in
+ isolation and provides mock objects. The test case class for Service objects is
+ {@link android.test.ServiceTestCase}. Since the Service class assumes that it is separate
+ from its clients, you can test a Service object without using instrumentation.
+</p>
+<p>
+ This document describes techniques for testing Service objects. If you aren't familiar with the
+ Service class, please read <a href="{@docRoot}guide/topics/fundamentals.html">
+ Application Fundamentals</a>. If you aren't familiar with Android testing, please read
+ <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Fundamentals</a>,
+ the introduction to the Android testing and instrumentation framework.
+</p>
+<h2 id="DesignAndTest">Service Design and Testing</h2>
+<p>
+ When you design a Service, you should consider how your tests can examine the various states
+ of the Service lifecycle. If the lifecycle methods that start up your Service, such as
+ {@link android.app.Service#onCreate() onCreate()} or
+ {@link android.app.Service#onStartCommand(Intent, int, int) onStartCommand()} do not normally
+ set a global variable to indicate that they were successful, you may want to provide such a
+ variable for testing purposes.
+</p>
+<p>
+ Most other testing is facilitated by the methods in the {@link android.test.ServiceTestCase}
+ test case class. For example, the {@link android.test.ServiceTestCase#getService()} method
+ returns a handle to the Service under test, which you can test to confirm that the Service is
+ running even at the end of your tests.
+</p>
+<h2 id="ServiceTestCase">ServiceTestCase</h2>
+<p>
+ {@link android.test.ServiceTestCase} extends the JUnit {@link junit.framework.TestCase} class
+ with with methods for testing application permissions and for controlling the application and
+ Service under test. It also provides mock application and Context objects that isolate your
+ test from the rest of the system.
+</p>
+<p>
+ {@link android.test.ServiceTestCase} defers initialization of the test environment until you
+ call {@link android.test.ServiceTestCase#startService(Intent) ServiceTestCase.startService()} or
+ {@link android.test.ServiceTestCase#bindService(Intent) ServiceTestCase.bindService()}. This
+ allows you to set up your test environment, particularly your mock objects, before the Service
+ is started.
+</p>
+<p>
+ Notice that the parameters to <code>ServiceTestCase.bindService()</code>are different from
+ those for <code>Service.bindService()</code>. For the <code>ServiceTestCase</code> version,
+ you only provide an Intent. Instead of returning a boolean,
+ <code>ServiceTestCase.bindService()</code> returns an object that subclasses
+ {@link android.os.IBinder}.
+</p>
+<p>
+ The {@link android.test.ServiceTestCase#setUp()} method for {@link android.test.ServiceTestCase}
+ is called before each test. It sets up the test fixture by making a copy of the current system
+ Context before any test methods touch it. You can retrieve this Context by calling
+ {@link android.test.ServiceTestCase#getSystemContext()}. If you override this method, you must
+ call <code>super.setUp()</code> as the first statement in the override.
+</p>
+<p>
+ The methods {@link android.test.ServiceTestCase#setApplication(Application) setApplication()}
+ and {@link android.test.AndroidTestCase#setContext(Context)} setContext()} allow you to set
+ a mock Context or mock Application (or both) for the Service, before you start it. These mock
+ objects are described in <a href="#MockObjects">Mock object classes</a>.
+</p>
+<p>
+ By default, {@link android.test.ServiceTestCase} runs the test method
+ {@link android.test.AndroidTestCase#testAndroidTestCaseSetupProperly()}, which asserts that
+ the base test case class successfully set up a Context before running.
+</p>
+<h2 id="MockObjects">Mock object classes</h2>
+<p>
+ <code>ServiceTestCase</code> assumes that you will use a mock Context or mock Application
+ (or both) for the test environment. These objects isolate the test environment from the
+ rest of the system. If you don't provide your own instances of these objects before you
+ start the Service, then {@link android.test.ServiceTestCase} will create its own internal
+ instances and inject them into the Service. You can override this behavior by creating and
+ injecting your own instances before starting the Service
+</p>
+<p>
+ To inject a mock Application object into the Service under test, first create a subclass of
+ {@link android.test.mock.MockApplication}. <code>MockApplication</code> is a subclass of
+ {@link android.app.Application} in which all the methods throw an Exception, so to use it
+ effectively you subclass it and override the methods you need. You then inject it into the
+ Service with the
+ {@link android.test.ServiceTestCase#setApplication(Application) setApplication()} method.
+ This mock object allows you to control the application values that the Service sees, and
+ isolates it from the real system. In addition, any hidden dependencies your Service has on
+ its application reveal themselves as exceptions when you run the test.
+</p>
+<p>
+ You inject a mock Context into the Service under test with the
+ {@link android.test.AndroidTestCase#setContext(Context) setContext()} method. The mock
+ Context classes you can use are described in more detail in
+ <a href="{@docRoot}guide/topics/testing/testing_android.html#MockObjectClasses">
+ Testing Fundamentals</a>.
+</p>
+<h2 id="TestAreas">What to Test</h2>
+<p>
+ The topic <a href="{@docRoot}guide/topics/testing/what_to_test.html">What To Test</a>
+ lists general considerations for testing Android components.
+ Here are some specific guidelines for testing a Service:
+</p>
+<ul>
+ <li>
+ Ensure that the {@link android.app.Service#onCreate()} is called in response to
+ {@link android.content.Context#startService(Intent) Context.startService()} or
+ {@link android.content.Context#bindService(Intent,ServiceConnection,int) Context.bindService()}.
+ Similarly, you should ensure that {@link android.app.Service#onDestroy()} is called in
+ response to {@link android.content.Context#stopService(Intent) Context.stopService()},
+ {@link android.content.Context#unbindService(ServiceConnection) Context.unbindService()},
+ {@link android.app.Service#stopSelf()}, or
+ {@link android.app.Service#stopSelfResult(int) stopSelfResult()}.
+ </li>
+ <li>
+ Test that your Service correctly handles multiple calls from
+ <code>Context.startService()</code>. Only the first call triggers
+ <code>Service.onCreate()</code>, but all calls trigger a call to
+ <code>Service.onStartCommand()</code>.
+ <p>
+ In addition, remember that <code>startService()</code> calls don't
+ nest, so a single call to <code>Context.stopService()</code> or
+ <code>Service.stopSelf()</code> (but not <code>stopSelf(int)</code>)
+ will stop the Service. You should test that your Service stops at the correct point.
+ </p>
+ </li>
+ <li>
+ Test any business logic that your Service implements. Business logic includes checking for
+ invalid values, financial and arithmetic calculations, and so forth.
+ </li>
+</ul>
diff --git a/docs/html/guide/topics/testing/testing_android.jd b/docs/html/guide/topics/testing/testing_android.jd
index 935aaf9..1d5f911 100755
--- a/docs/html/guide/topics/testing/testing_android.jd
+++ b/docs/html/guide/topics/testing/testing_android.jd
@@ -1,4 +1,4 @@
-page.title=Testing and Instrumentation
+page.title=Testing Fundamentals
@jd:body
<div id="qv-wrapper">
@@ -6,65 +6,62 @@
<h2>In this document</h2>
<ol>
<li>
- <a href="#Overview">Overview</a>
+ <a href="#TestStructure">Test Structure</a>
+ </li>
+ <li>
+ <a href="#TestProjects">Test Projects</a>
</li>
<li>
<a href="#TestAPI">The Testing API</a>
<ol>
<li>
- <a href="#Extensions">JUnit test case classes</a>
+ <a href="#JUnit">JUnit</a>
</li>
<li>
- <a href="#Instrumentation">Instrumentation test case classes</a>
+ <a href="#Instrumentation">Instrumentation</a>
</li>
<li>
- <a href="#Assert">Assert classes</a>
+ <a href="#TestCaseClasses">Test case classes</a>
</li>
<li>
- <a href="#MockObjects">Mock object classes</a>
+ <a href="#AssertionClasses">Assertion classes</a>
</li>
- <li>
- <a href="#InstrumentationTestRunner">Instrumentation Test Runner</a>
- </li>
+ <li>
+ <a href="#MockObjectClasses">Mock object classes</a>
+ </li>
</ol>
</li>
<li>
- <a href="#TestEnviroment">Working in the Test Environment</a>
+ <a href="#InstrumentationTestRunner">Running Tests</a>
</li>
<li>
- <a href="#TestAreas">What to Test</a>
+ <a href="#TestResults">Seeing Test Results</a>
</li>
<li>
- <a href="#UITesting">Appendix: UI Testing Notes</a>
- <ol>
- <li>
- <a href="#RunOnUIThread">Testing on the UI thread</a>
- </li>
- <li>
- <a href="#NotouchMode">Turning off touch mode</a>
- </li>
- <li>
- <a href="#UnlockDevice">Unlocking the Emulator or Device</a>
- </li>
- <li>
- <a href="#UITestTroubleshooting">Troubleshooting UI tests</a>
- </li>
- </ol>
+ <a href="#Monkeys">Monkey and MonkeyRunner</a>
+ </li>
+ <li>
+ <a href="#PackageNames">Working With Package Names</a>
+ </li>
+ <li>
+ <a href="#WhatToTest">What To Test</a>
+ </li>
+ <li>
+ <a href="#NextSteps">Next Steps</a>
</li>
</ol>
<h2>Key classes</h2>
<ol>
<li>{@link android.test.InstrumentationTestRunner}</li>
- <li>{@link android.test.ActivityInstrumentationTestCase2}</li>
- <li>{@link android.test.ActivityUnitTestCase}</li>
- <li>{@link android.test.ApplicationTestCase}</li>
- <li>{@link android.test.ProviderTestCase2}</li>
- <li>{@link android.test.ServiceTestCase}</li>
+ <li>{@link android.test}</li>
+ <li>{@link android.test.mock}</li>
+ <li>{@link junit.framework}</li>
</ol>
<h2>Related tutorials</h2>
<ol>
<li>
- <a href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">Hello, Testing</a>
+ <a href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">
+ Hello, Testing</a>
</li>
<li>
<a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
@@ -73,445 +70,590 @@
<h2>See also</h2>
<ol>
<li>
- <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in Eclipse, with ADT</a>
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">
+ Testing in Eclipse, with ADT</a>
</li>
<li>
- <a href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other IDEs</a>
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in Other IDEs</a>
</li>
</ol>
</div>
</div>
-
-<p>Android includes a powerful set of testing tools that extend the
-industry-standard JUnit test framework with features specific to the Android
-environment. Although you can test an Android application with JUnit, the
-Android tools allow you to write much more sophisticated tests for every aspect
-of your application, both at the unit and framework levels.</p>
-
-<p>Key features of the Android testing environment include:</p>
-
+<p>
+ The Android testing framework, an integral part of the development environment,
+ provides an architecture and powerful tools that help you test every aspect of your application
+ at every level from unit to framework.
+</p>
+<p>
+ The testing framework has these key features:
+</p>
<ul>
- <li>Android extensions to the JUnit framework that provide access to Android
-system objects.</li>
- <li>An instrumentation framework that lets tests control and examine the
-application.</li>
- <li>Mock versions of commonly-used Android system objects.</li>
- <li>Tools for running single tests or test suites, with or without
-instrumentation.</li>
- <li>Support for managing tests and test projects in the ADT Plugin for Eclipse
-and at the command line.</li>
+ <li>
+ Android test suites are based on JUnit. You can use plain JUnit to test a class that doesn't
+ call the Android API, or Android's JUnit extensions to test Android components. If you're
+ new to Android testing, you can start with general-purpose test case classes such as {@link
+ android.test.AndroidTestCase} and then go on to use more sophisticated classes.
+ </li>
+ <li>
+ The Android JUnit extensions provide component-specific test case classes. These classes
+ provide helper methods for creating mock objects and methods that help you control the
+ lifecycle of a component.
+ </li>
+ <li>
+ Test suites are contained in test packages that are similar to main application packages, so
+ you don't need to learn a new set of tools or techniques for designing and building tests.
+ </li>
+ <li>
+ The SDK tools for building and tests are available in Eclipse with ADT, and also in
+ command-line form for use with other IDES. These tools get information from the project of
+ the application under test and use this information to automatically create the build files,
+ manifest file, and directory structure for the test package.
+ </li>
+ <li>
+ The SDK also provides
+ <a href="{@docRoot}guide/topics/testing/monkeyrunner.html">MonkeyRunner</a>, an API for
+ testing devices with Jython scripts, and <a
+ href="{@docRoot}guide/developing/tools/monkey.html">Monkey</a>, a command-line tool for
+ stress-testing UIs by sending pseudo-random events to a device.
+ </li>
</ul>
-
-<p>This document is an overview of the Android testing environment and the way
-you use it. The document assumes you have a basic knowledge of Android
-application programming and JUnit testing methodology.</p>
-
-<h2 id="Overview">Overview</h2>
-
-<p> At the heart of the Android testing environment is an instrumentation
-framework that your test application uses to precisely control the application
-under test. With instrumentation, you can set up mock system objects such as
-Contexts before the main application starts, control your application at various
-points of its lifecycle, send UI events to the application, and examine the
-application's state during its execution. The instrumentation framework
-accomplishes this by running both the main application and the test application
-in the same process. </p>
-
-<p>Your test application is linked to the application under test by means of an
-<a
-href="{@docRoot}guide/topics/manifest/instrumentation-element.html"><code><instrumentation></code></a>
-element in the test application's manifest file. The attributes of the element
-specify the package name of the application under test and also tell Android how
-to run the test application. Instrumentation is described in more detail in the
-section <a href="#InstrumentationTestRunner">Instrumentation Test
-Runner</a>.</p>
-
-<p>The following diagram summarizes the Android testing environment:</p>
-
-<img src="{@docRoot}images/testing/android_test_framework.png"/>
-
-<p>In Android, test applications are themselves Android applications, so you
-write them in much the same way as the application you are testing. The SDK
-tools help you create a main application project and its test project at the same
-time. You can run Android tests within Eclipse with ADT or from the command
-line. Eclipse with ADT provides an extensive set of tools for creating tests,
-running them, and viewing their results. You can also use the <code>adb</code>
-tool to run tests, or use a built-in Ant target.</p>
-
-<p>To learn how to set up and run tests in Eclipse, please refer to <a
-href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in
-Eclipse, with ADT</a>. If you're not working in Eclipse, refer to <a
-href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other
-IDEs</a>.</p>
-
-<p>If you want a step-by-step introduction to Android testing, try one of the
-testing tutorials:</p>
-
-<ul>
- <li>The <a
-href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">Hello,
-Testing</a> tutorial introduces basic testing concepts and procedures in the
-context of the Hello, World application.</li>
- <li>The <a
-href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity
-Testing</a> tutorial is an excellent follow-up to the Hello, Testing tutorial.
-It guides you through a more complex testing scenario that you develop against a
-more realistic application.</li>
-</ul>
-
+<p>
+ This document describes the fundamentals of the Android testing framework, including the
+ structure of tests, the APIs that you use to develop tests, and the tools that you use to run
+ tests and view results. The document assumes you have a basic knowledge of Android application
+ programming and JUnit testing methodology.
+</p>
+<p>
+ The following diagram summarizes the testing framework:
+</p>
+<div style="width: 70%; margin-left:auto; margin-right:auto;">
+<a href="{@docRoot}images/testing/test_framework.png">
+ <img src="{@docRoot}images/testing/test_framework.png"
+ alt="The Android testing framework"/>
+</a>
+</div>
+<h2 id="TestStructure">Test Structure</h2>
+<p>
+ Android's build and test tools assume that test projects are organized into a standard
+ structure of tests, test case classes, test packages, and test projects.
+</p>
+<p>
+ Android testing is based on JUnit. In general, a JUnit test is a method whose
+ statements test a part of the application under test. You organize test methods into classes
+ called test cases (or test suites). Each test is an isolated test of an individual module in
+ the application under test. Each class is a container for related test methods, although it
+ often provides helper methods as well.
+</p>
+<p>
+ In JUnit, you build one or more test source files into a class file. Similarly, in Android you
+ use the SDK's build tools to build one or more test source files into class files in an
+ Android test package. In JUnit, you use a test runner to execute test classes. In Android, you
+ use test tools to load the test package and the application under test, and the tools then
+ execute an Android-specific test runner.
+</p>
+<h2 id="TestProjects">Test Projects</h2>
+<p>
+ Tests, like Android applications, are organized into projects.
+</p>
+<p>
+ A test project is a directory or Eclipse project in which you create the source code, manifest
+ file, and other files for a test package. The Android SDK contains tools for Eclipse with ADT
+ and for the command line that create and update test projects for you. The tools create the
+ directories you use for source code and resources and the manifest file for the test package.
+ The command-line tools also create the Ant build files you need.
+</p>
+<p>
+ You should always use Android tools to create a test project. Among other benefits,
+ the tools:
+</p>
+ <ul>
+ <li>
+ Automatically set up your test package to use
+ {@link android.test.InstrumentationTestRunner} as the test case runner. You must use
+ <code>InstrumentationTestRunner</code> (or a subclass) to run JUnit tests.
+ </li>
+ <li>
+ Create an appropriate name for the test package. If the application
+ under test has a package name of <code>com.mydomain.myapp</code>, then the
+ Android tools set the test package name to <code>com.mydomain.myapp.test</code>. This
+ helps you identify their relationship, while preventing conflicts within the system.
+ </li>
+ <li>
+ Automatically create the proper build files, manifest file, and directory
+ structure for the test project. This helps you to build the test package without
+ having to modify build files and sets up the linkage between your test package and
+ the application under test.
+ The
+ </li>
+ </ul>
+<p>
+ You can create a test project anywhere in your file system, but the best approach is to
+ add the test project so that its root directory <code>tests/</code> is at the same level
+ as the <code>src/</code> directory of the main application's project. This helps you find the
+ tests associated with an application. For example, if your application project's root directory
+ is <code>MyProject</code>, then you should use the following directory structure:
+</p>
+<pre class="classic no-pretty-print">
+ MyProject/
+ AndroidManifest.xml
+ res/
+ ... (resources for main application)
+ src/
+ ... (source code for main application) ...
+ tests/
+ AndroidManifest.xml
+ res/
+ ... (resources for tests)
+ src/
+ ... (source code for tests)
+</pre>
<h2 id="TestAPI">The Testing API</h2>
<p>
- For writing tests and test applications in the Java programming language, Android provides a
- testing API that is based in part on the JUnit test framework. Adding to that, Android includes
- a powerful instrumentation framework that lets your tests access the state and runtime objects
- of the application under tests.
+ The Android testing API is based on the JUnit API and extended with a instrumentation
+ framework and Android-specific testing classes.
</p>
-<p>The sections below describe the major components of the testing API available in Android.</p>
-<h3 id="Extensions">JUnit test case classes</h3>
+<h3 id="JUnit">JUnit</h3>
<p>
- Some of the classes in the testing API extend the JUnit {@link junit.framework.TestCase TestCase} but do not use the instrumentation framework. These classes
- contain methods for accessing system objects such as the Context of the application under test. With this Context, you can look at its resources, files, databases,
- and so forth. The base class is {@link android.test.AndroidTestCase}, but you usually use a subclass associated with a particular component.
-<p>
- The subclasses are:
-</p>
- <ul>
- <li>
- {@link android.test.ApplicationTestCase} - A class for testing an entire application. It allows you to inject a mock Context into the application,
- set up initial test parameters before the application starts, and examine the application after it finishes but before it is destroyed.
- </li>
- <li>
- {@link android.test.ProviderTestCase2} - A class for isolated testing of a single {@link android.content.ContentProvider}. Since it is restricted to using a
- {@link android.test.mock.MockContentResolver} for the provider, and it injects an {@link android.test.IsolatedContext}, your provider testing is isolated
- from the rest of the OS.
- </li>
- <li>
- {@link android.test.ServiceTestCase} - a class for isolated testing of a single {@link android.app.Service}. You can inject a mock Context or
- mock Application (or both), or let Android provide you a full Context and a {@link android.test.mock.MockApplication}.
- </li>
- </ul>
-<h3 id="Instrumentation">Instrumentation test case classes</h3>
-<p>
- The API for testing activities extends the JUnit {@link junit.framework.TestCase TestCase} class and also uses the instrumentation framework. With instrumentation,
- Android can automate UI testing by sending events to the application under test, precisely control the start of an activity, and monitor the state of the
- activity during its life cycle.
+ You can use the JUnit {@link junit.framework.TestCase TestCase} class to do unit testing on
+ a plain Java object. <code>TestCase</code> is also the base class for
+ {@link android.test.AndroidTestCase}, which you can use to test Android-dependent objects.
+ Besides providing the JUnit framework, AndroidTestCase offers Android-specific setup,
+ teardown, and helper methods.
</p>
<p>
- The base class is {@link android.test.InstrumentationTestCase}. All of its subclasses have the ability to send a keystroke or touch event to the UI of the application
- under test. The subclasses can also inject a mock Intent.
- The subclasses are:
+ You use the JUnit {@link junit.framework.Assert} class to display test results.
+ The assert methods compare values you expect from a test to the actual results and
+ throw an exception if the comparison fails. Android also provides a class of assertions that
+ extend the possible types of comparisons, and another class of assertions for testing the UI.
+ These are described in more detail in the section <a href="#AssertionClasses">
+ Assertion classes</a>
</p>
- <ul>
- <li>
- {@link android.test.ActivityTestCase} - A base class for activity test classes.
- </li>
- <li>
- {@link android.test.SingleLaunchActivityTestCase} - A convenience class for testing a single activity.
- It invokes {@link junit.framework.TestCase#setUp() setUp()} and {@link junit.framework.TestCase#tearDown() tearDown()} only
- once, instead of once per method call. Use it when all of your test methods run against the same activity.
- </li>
- <li>
- {@link android.test.SyncBaseInstrumentation} - A class that tests synchronization of a content provider. It uses instrumentation to cancel and disable
- existing synchronizations before starting the test synchronization.
- </li>
- <li>
- {@link android.test.ActivityUnitTestCase} - This class does an isolated test of a single activity. With it, you can inject a mock context or application, or both.
- It is intended for doing unit tests of an activity, and is the activity equivalent of the test classes described in <a href="#Extensions">JUnit test case classes</a>.
- <p> Unlike the other instrumentation classes, this test class cannot inject a mock Intent.</p>
- </li>
- <li>
- {@link android.test.ActivityInstrumentationTestCase2} - This class tests a single activity within the normal system environment.
- You cannot inject a mock Context, but you can inject mock Intents. Also, you can run a test method on the UI thread (the main thread of the application under test),
- which allows you to send key and touch events to the application UI.
- </li>
- </ul>
-<h3 id="Assert">Assert classes</h3>
<p>
- Android also extends the JUnit {@link junit.framework.Assert} class that is the basis of <code>assert()</code> calls in tests.
- There are two extensions to this class, {@link android.test.MoreAsserts} and {@link android.test.ViewAsserts}:
+ To learn more about JUnit, you can read the documentation on the
+ <a href="http://www.junit.org">junit.org</a> home page.
+ Note that the Android testing API supports JUnit 3 code style, but not JUnit 4. Also, you must
+ use Android's instrumented test runner {@link android.test.InstrumentationTestRunner} to run
+ your test case classes. This test runner is described in the
+ section <a href="#InstrumentationTestRunner">Running Tests</a>.
+</p>
+<h3 id="Instrumentation">Instrumentation</h3>
+<p>
+ Android instrumentation is a set of control methods or "hooks" in the Android system. These hooks
+ control an Android component independently of its normal lifecycle. They also control how
+ Android loads applications.
+</p>
+<p>
+ Normally, an Android component runs in a lifecycle determined by the system. For example, an
+ Activity object's lifecycle starts when the Activity is activated by an Intent. The object's
+ <code>onCreate()</code> method is called, followed by <code>onResume()</code>. When the user
+ starts another application, the <code>onPause()</code> method is called. If the Activity
+ code calls the <code>finish()</code> method, the <code>onDestroy()</code> method is called.
+ The Android framework API does not provide a way for your code to invoke these callback
+ methods directly, but you can do so using instrumentation.
+</p>
+<p>
+ Also, the system runs all the components of an application into the same
+ process. You can allow some components, such as content providers, to run in a separate process,
+ but you can't force an application to run in the same process as another application that is
+ already running.
+</p>
+<p>
+ With Android instrumentation, though, you can invoke callback methods in your test code.
+ This allows you to run through the lifecycle of a component step by step, as if you were
+ debugging the component. The following test code snippet demonstrates how to use this to
+ test that an Activity saves and restores its state:
+</p>
+<a name="ActivitySnippet"></a>
+<pre>
+ // Start the main activity of the application under test
+ mActivity = getActivity();
+
+ // Get a handle to the Activity object's main UI widget, a Spinner
+ mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01);
+
+ // Set the Spinner to a known position
+ mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);
+
+ // Stop the activity - The onDestroy() method should save the state of the Spinner
+ mActivity.finish();
+
+ // Re-start the Activity - the onResume() method should restore the state of the Spinner
+ mActivity = getActivity();
+
+ // Get the Spinner's current position
+ int currentPosition = mActivity.getSpinnerPosition();
+
+ // Assert that the current position is the same as the starting position
+ assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
+</pre>
+<p>
+ The key method used here is
+ {@link android.test.ActivityInstrumentationTestCase2#getActivity()}, which is a
+ part of the instrumentation API. The Activity under test is not started until you call this
+ method. You can set up the test fixture in advance, and then call this method to start the
+ Activity.
+</p>
+<p>
+ Also, instrumentation can load both a test package and the application under test into the
+ same process. Since the application components and their tests are in the same process, the
+ tests can invoke methods in the components, and modify and examine fields in the components.
+</p>
+<h3 id="TestCaseClasses">Test case classes</h3>
+<p>
+ Android provides several test case classes that extend {@link junit.framework.TestCase} and
+ {@link junit.framework.Assert} with Android-specific setup, teardown, and helper methods.
+</p>
+<h4 id="AndroidTestCase">AndroidTestCase</h4>
+<p>
+ A useful general test case class, especially if you are
+ just starting out with Android testing, is {@link android.test.AndroidTestCase}. It extends
+ both {@link junit.framework.TestCase} and {@link junit.framework.Assert}. It provides the
+ JUnit-standard <code>setUp()</code> and <code>tearDown()</code> methods, as well as well as
+ all of JUnit's Assert methods. In addition, it provides methods for testing permissions, and a
+ method that guards against memory leaks by clearing out certain class references.
+</p>
+<h4 id="ComponentTestCase">Component-specific test cases</h4>
+<p>
+ A key feature of the Android testing framework is its component-specific test case classes.
+ These address specific component testing needs with methods for fixture setup and
+ teardown and component lifecycle control. They also provide methods for setting up mock objects.
+ These classes are described in the component-specific testing topics:
</p>
<ul>
- <li>
- The <code>MoreAsserts</code> class contains more powerful assertions such as {@link android.test.MoreAsserts#assertContainsRegex} that does regular expression matching.
- </li>
- <li>
- The {@link android.test.ViewAsserts} class contains useful assertions about Android Views, such as {@link android.test.ViewAsserts#assertHasScreenCoordinates} that tests if a View has a particular X and Y
- position on the visible screen. These asserts simplify testing of geometry and alignment in the UI.
- </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/activity_testing.html">Activity Testing</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/contentprovider_testing.html">
+ Content Provider Testing</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/service_testing.html">Service Testing</a>
+ </li>
</ul>
-<h3 id="MockObjects">Mock object classes</h3>
- <p>
- Android has convenience classes for creating mock system objects such as applications, contexts, content resolvers, and resources. Android also provides
- methods in some test classes for creating mock Intents. Use these mocks to facilitate dependency injection, since they are easier to use than creating their
- real counterparts. These convenience classes are found in {@link android.test} and {@link android.test.mock}. They are:
- </p>
+<p>
+ Android does not provide a separate test case class for BroadcastReceiver. Instead, test a
+ BroadcastReceiver by testing the component that sends it Intent objects, to verify that the
+ BroadcastReceiver responds correctly.
+</p>
+<h4 id="ApplicationTestCase">ApplicationTestCase</h4>
+<p>
+ You use the {@link android.test.ApplicationTestCase} test case class to test the setup and
+ teardown of {@link android.app.Application} objects. These objects maintain the global state of
+ information that applies to all the components in an application package. The test case can
+ be useful in verifying that the <application> element in the manifest file is correctly
+ set up. Note, however, that this test case does not allow you to control testing of the
+ components within your application package.
+</p>
+<h4 id="InstrumentationTestCase">InstrumentationTestCase</h4>
+<p>
+ If you want to use instrumentation methods in a test case class, you must use
+ {@link android.test.InstrumentationTestCase} or one of its subclasses. The
+ {@link android.app.Activity} test cases extend this base class with other functionality that
+ assists in Activity testing.
+</p>
+
+<h3 id="AssertionClasses">Assertion classes</h3>
+<p>
+ Because Android test case classes extend JUnit, you can use assertion methods to display the
+ results of tests. An assertion method compares an actual value returned by a test to an
+ expected value, and throws an AssertionException if the comparison test fails. Using assertions
+ is more convenient than doing logging, and provides better test performance.
+</p>
+<p>
+ Besides the JUnit {@link junit.framework.Assert} class methods, the testing API also provides
+ the {@link android.test.MoreAsserts} and {@link android.test.ViewAsserts} classes:
+</p>
+<ul>
+ <li>
+ {@link android.test.MoreAsserts} contains more powerful assertions such as
+ {@link android.test.MoreAsserts#assertContainsRegex}, which does regular expression
+ matching.
+ </li>
+ <li>
+ {@link android.test.ViewAsserts} contains useful assertions about Views. For example
+ it contains {@link android.test.ViewAsserts#assertHasScreenCoordinates} that tests if a View
+ has a particular X and Y position on the visible screen. These asserts simplify testing of
+ geometry and alignment in the UI.
+ </li>
+</ul>
+<h3 id="MockObjectClasses">Mock object classes</h3>
+<p>
+ To facilitate dependency injection in testing, Android provides classes that create mock system
+ objects such as {@link android.content.Context} objects,
+ {@link android.content.ContentProvider} objects, {@link android.content.ContentResolver}
+ objects, and {@link android.app.Service} objects. Some test cases also provide mock
+ {@link android.content.Intent} objects. You use these mocks both to isolate tests
+ from the rest of the system and to facilitate dependency injection for testing. These classes
+ are found in the Java packages {@link android.test} and {@link android.test.mock}.
+</p>
+<p>
+ Mock objects isolate tests from a running system by stubbing out or overriding
+ normal operations. For example, a {@link android.test.mock.MockContentResolver}
+ replaces the normal resolver framework with its own local framework, which is isolated
+ from the rest of the system. MockContentResolver also also stubs out the
+ {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)} method
+ so that observer objects outside the test environment are not accidentally triggered.
+</p>
+<p>
+ Mock object classes also facilitate dependency injection by providing a subclass of the
+ normal object that is non-functional except for overrides you define. For example, the
+ {@link android.test.mock.MockResources} object provides a subclass of
+ {@link android.content.res.Resources} in which all the methods throw Exceptions when called.
+ To use it, you override only those methods that must provide information.
+</p>
+<p>
+ These are the mock object classes available in Android:
+</p>
+<h4 id="SimpleMocks">Simple mock object classes</h4>
+<p>
+ {@link android.test.mock.MockApplication}, {@link android.test.mock.MockContext},
+ {@link android.test.mock.MockContentProvider}, {@link android.test.mock.MockCursor},
+ {@link android.test.mock.MockDialogInterface}, {@link android.test.mock.MockPackageManager}, and
+ {@link android.test.mock.MockResources} provide a simple and useful mock strategy. They are
+ stubbed-out versions of the corresponding system object class, and all of their methods throw an
+ {@link java.lang.UnsupportedOperationException} exception if called. To use them, you override
+ the methods you need in order to provide mock dependencies.
+</p>
+<p class="Note"><strong>Note:</strong>
+ {@link android.test.mock.MockContentProvider}
+ and {@link android.test.mock.MockCursor} are new as of API level 8.
+</p>
+<h4 id="ResolverMocks">Resolver mock objects</h4>
+<p>
+ {@link android.test.mock.MockContentResolver} provides isolated testing of content providers by
+ masking out the normal system resolver framework. Instead of looking in the system to find a
+ content provider given an authority string, MockContentResolver uses its own internal table. You
+ must explicitly add providers to this table using
+ {@link android.test.mock.MockContentResolver#addProvider(String,ContentProvider)}.
+</p>
+<p>
+ With this feature, you can associate a mock content provider with an authority. You can create
+ an instance of a real provider but use test data in it. You can even set the provider for an
+ authority to <code>null</code>. In effect, a MockContentResolver object isolates your test
+ from providers that contain real data. You can control the
+ function of the provider, and you can prevent your test from affecting real data.
+</p>
+<h3 id="ContextMocks">Contexts for testing</h3>
+<p>
+ Android provides two Context classes that are useful for testing:
+</p>
+<ul>
+ <li>
+ {@link android.test.IsolatedContext} provides an isolated {@link android.content.Context},
+ File, directory, and database operations that use this Context take place in a test area.
+ Though its functionality is limited, this Context has enough stub code to respond to
+ system calls.
+ <p>
+ This class allows you to test an application's data operations without affecting real
+ data that may be present on the device.
+ </p>
+ </li>
+ <li>
+ {@link android.test.RenamingDelegatingContext} provides a Context in which
+ most functions are handled by an existing {@link android.content.Context}, but
+ file and database operations are handled by a {@link android.test.IsolatedContext}.
+ The isolated part uses a test directory and creates special file and directory names.
+ You can control the naming yourself, or let the constructor determine it automatically.
+ <p>
+ This object provides a quick way to set up an isolated area for data operations,
+ while keeping normal functionality for all other Context operations.
+ </p>
+ </li>
+</ul>
+<h2 id="InstrumentationTestRunner">Running Tests</h2>
+<p>
+ Test cases are run by a test runner class that loads the test case class, set ups,
+ runs, and tears down each test. An Android test runner must also be instrumented, so that
+ the system utility for starting applications can control how the test package
+ loads test cases and the application under test. You tell the Android platform
+ which instrumented test runner to use by setting a value in the test package's manifest file.
+</p>
+<p>
+ {@link android.test.InstrumentationTestRunner} is the primary Android test runner class. It
+ extends the JUnit test runner framework and is also instrumented. It can run any of the test
+ case classes provided by Android and supports all possible types of testing.
+</p>
+<p>
+ You specify <code>InstrumentationTestRunner</code> or a subclass in your test package's
+ manifest file, in the <a href="{@docRoot}guide/topics/manifest/instrumentation-element.html">
+ instrumentation</a> element. Also, <code>InstrumentationTestRunner</code> code resides
+ in the shared library <code>android.test.runner</code>, which is not normally linked to
+ Android code. To include it, you must specify it in a
+ <a href="{@docRoot}guide/topics/manifest/uses-library-element.html">uses-library</a> element.
+ You do not have to set up these elements yourself. Both Eclipse with ADT and the
+ <code>android</code> command-line tool construct them automatically and add them to your
+ test package's manifest file.
+</p>
+<p class="Note">
+ <strong>Note:</strong> If you use a test runner other than
+ <code>InstrumentationTestRunner</code>, you must change the <instrumentation>
+ element to point to the class you want to use.
+</p>
+<p>
+ To run {@link android.test.InstrumentationTestRunner}, you use internal system classes called by
+ Android tools. When you run a test in Eclipse with ADT, the classes are called automatically.
+ When you run a test from the command line, you run these classes with
+ <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge (adb)</a>.
+</p>
+<p>
+ The system classes load and start the test package, kill any processes that
+ are running an instance of the application under test, and then load a new instance of the
+ application under test. They then pass control to
+ {@link android.test.InstrumentationTestRunner}, which runs
+ each test case class in the test package. You can also control which test cases and
+ methods are run using settings in Eclipse with ADT, or using flags with the command-line tools.
+</p>
+<p>
+ Neither the system classes nor {@link android.test.InstrumentationTestRunner} run
+ the application under test. Instead, the test case does this directly. It either calls methods
+ in the application under test, or it calls its own methods that trigger lifecycle events in
+ the application under test. The application is under the complete control of the test case,
+ which allows it to set up the test environment (the test fixture) before running a test. This
+ is demonstrated in the previous <a href="#ActivitySnippet">code snippet</a> that tests an
+ Activity that displays a Spinner widget.
+</p>
+<p>
+ To learn more about running tests, please read the topics
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html"">
+ Testing in Eclipse, with ADT</a> or
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html">
+ Testing in Other IDes</a>.
+</p>
+<h2 id="TestResults">Seeing Test Results</h2>
+<p>
+ The Android testing framework returns test results back to the tool that started the test.
+ If you run a test in Eclipse with ADT, the results are displayed in a new JUnit view pane. If
+ you run a test from the command line, the results are displayed in <code>STDOUT</code>. In
+ both cases, you see a test summary that displays the name of each test case and method that
+ was run. You also see all the assertion failures that occurred. These include pointers to the
+ line in the test code where the failure occurred. Assertion failures also list the expected
+ value and actual value.
+</p>
+<p>
+ The test results have a format that is specific to the IDE that you are using. The test
+ results format for Eclipse with ADT is described in
+ <a href="{@docRoot}guide/developing/testing/testing_eclipse.html#RunTestEclipse">
+ Testing in Eclipse, with ADT</a>. The test results format for tests run from the
+ command line is described in
+ <a href="{@docRoot}guide/developing/testing/testing_otheride.html#RunTestsCommand">
+ Testing in Other IDEs</a>.
+</p>
+<h2 id="Monkeys">Monkey and MonkeyRunner</h2>
+<p>
+ The SDK provides two tools for functional-level application testing:
+</p>
<ul>
- <li>
- {@link android.test.IsolatedContext} - Mocks a Context so that the application using it runs in isolation.
- At the same time, it has enough stub code to satisfy OS code that tries to communicate with contexts. This class is useful in unit testing.
- </li>
- <li>
- {@link android.test.RenamingDelegatingContext} - Delegates most context functions to an existing, normal context while changing the default file and database
- names in the context. Use this to test file and database operations with a normal system context, using test names.
- </li>
- <li>
- {@link android.test.mock.MockApplication}, {@link android.test.mock.MockContentResolver}, {@link android.test.mock.MockContext},
- {@link android.test.mock.MockDialogInterface}, {@link android.test.mock.MockPackageManager},
- {@link android.test.mock.MockResources} - Classes that create mock Android system objects for use in testing. They expose only those methods that are
- useful in managing the object. The default implementations of these methods simply throw an Exception. You are expected to extend the classes and
- override any methods that are called by the application under test.
- </li>
+ <li>
+ <a href="{@docRoot}guide/developing/tools/monkey.html">Monkey</a> is a command-line
+ tool that sends pseudo-random streams of keystrokes, touches, and gestures to a
+ device. You run it with the <a href="{@docRoot}guide/developing/tools/adb.html">
+ Android Debug Bridge</a> (adb) tool. You use it to stress-test your application and
+ report back errors that are encountered. You can repeat a stream of events by
+ running the tool each time with the same random number seed.
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/testing/monkeyrunner.html">MonkeyRunner</a> is a
+ Jython API that you use in test programs written in Python. The API includes functions
+ for connecting to a device, installing and uninstalling packages, taking screenshots,
+ comparing two images, and running a test package against an application. Using the API
+ with Python, you can write a wide range of large, powerful, and complex tests.
+ </li>
</ul>
-<h3 id="InstrumentationTestRunner">Instrumentation Test Runner</h3>
+<h2 id="PackageNames">Working With Package names</h2>
<p>
- Android provides a custom class for running tests with instrumentation called called
- {@link android.test.InstrumentationTestRunner}. This class
- controls of the application under test, runs the test application and the main application in the same process, and routes
- test output to the appropriate place. Using instrumentation is key to the ability of <code>InstrumentationTestRunner</code> to control the entire test
- environment at runtime. Notice that you use this test runner even if your test class does not itself use instrumentation.
+ In the test environment, you work with both Android application package names and
+ Java package identifiers. Both use the same naming format, but they represent substantially
+ different entities. You need to know the difference to set up your tests correctly.
</p>
<p>
- When you run a test application, you first run a system utility called Activity Manager. Activity Manager uses the instrumentation framework to start and control the test runner, which in turn uses instrumentation to shut down any running instances
- of the main application, starts the test application, and then starts the main application in the same process. This allows various aspects of the test application to work directly with the main application.
+ An Android package name is a unique system name for a <code>.apk</code> file, set by the
+ "android:package" attribute of the <manifest> element in the package's
+ manifest. The Android package name of your test package must be different from the
+ Android package name of the application under test. By default, Android tools create the
+ test package name by appending ".test" to the package name of the application under test.
</p>
<p>
- If you are developing in Eclipse, the ADT plugin assists you in the setup of <code>InstrumentationTestRunner</code> or other test runners.
- The plugin UI prompts you to specify the test runner class to use, as well as the package name of the application under test.
- The plugin then adds an <code><instrumentation></code> element with appropriate attributes to the manifest file of the test application.
- Eclipse with ADT automatically starts a test application under the control of Activity Manager using instrumentation,
- and redirects the test output to the Eclipse window's JUnit view.
+ The test package also uses an Android package name to target the application package it
+ tests. This is set in the "android:targetPackage" attribute of the
+ <instrumentation> element in the test package's manifest.
</p>
<p>
- If you prefer working from the command line, you can use Ant and the <code>android</code>
- tool to help you set up your test projects. To run tests with instrumentation, you can access the
- Activity Manager through the <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug
- Bridge</a> (<code>adb</code>) tool and the output is directed to <code>STDOUT</code>.
-</p>
-<h2 id="TestEnviroment">Working in the Test Environment</h2>
-<p>
- The tests for an Android application are contained in a test application, which itself is an Android application. A test application resides in a separate Android project that has the
- same files and directories as a regular Android application. The test project is linked to the project of the application it tests
- (known as the application under test) by its manifest file.
+ A Java package identifier applies to a source file. This package name reflects the directory
+ path of the source file. It also affects the visibility of classes and members to each other.
</p>
<p>
- Each test application contains one or more test case classes based on an Android class for a
- particular type of component. The test case class contains methods that define tests on some part of the application under test. When you run the test application, Android
- starts it, loads the application under test into the same process, and then invokes each method in the test case class.
+ Android tools that create test projects set up an Android test package name for you.
+ From your input, the tools set up the test package name and the target package name for the
+ application under test. For these tools to work, the application project must already exist.
</p>
<p>
- The tools and procedures you use with testing depend on the development environment you are using. If you use Eclipse, then the ADT plug in for Eclipse provides tools that
- allow you to develop and run tests entirely within Eclipse. This is documented in the topic <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in Eclipse, with ADT</a>.
- If you use another development environment, then you use Android's command-line tools, as documented in the topic <a href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other IDEs</a>.
+ By default, these tools set the Java package identifier for the test class to be the same
+ as the Android package identifier. You may want to change this if you want to expose
+ members in the application under test by giving them package visibility. If you do this,
+ change only the Java package identifier, not the Android package names, and change only the
+ test case source files. Do not change the Java package name of the generated
+ <code>R.java</code> class in your test package, because it will then conflict with the
+ <code>R.java</code> class in the application under test. Do not change the Android package name
+ of your test package to be the same as the application it tests, because then their names
+ will no longer be unique in the system.
</p>
-<h3 id="TestProjects">Working with test projects</h3>
+<h2 id="WhatToTest">What to Test</h2>
<p>
- To start testing an Android application, you create a test project for it using Android tools. The tools create the project directory and the files and subdirectories needed.
- The tools also create a manifest file that links the application in the test project to the application under test. The procedure for creating a test project in Eclipse with
- ADT is documented in <a href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in Eclipse, with ADT</a>. The procedure for creating a test project for use with development
- tools other than Eclipse is documented in <a href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other IDEs</a>.
-</p>
-<h3 id="TestClasses">Working with test case classes</h3>
-<p>
- A test application contains one or more test case classes that extend an Android test case class. You choose a test case class based on the type of Android component you are testing and the
- tests you are doing. A test application can test different components, but each test case class is designed to test a single type of component.
- The Android test case classes are described in the section <a href="#TestAPI">The Testing API</a>.
+ The topic <a href="{@docRoot}guide/topics/testing/what_to_test.html">What To Test</a>
+ describes the key functionality you should test in an Android application, and the key
+ situations that might affect that functionality.
</p>
<p>
- Some Android components have more than one associated test case class. In this case, you choose among the available classes based on the type of tests you want to do. For activities,
- for example, you have the choice of either {@link android.test.ActivityInstrumentationTestCase2} or {@link android.test.ActivityUnitTestCase}.
-<p>
- <code>ActivityInstrumentationTestCase2</code> is designed to do functional testing, so it tests activities in a normal system infrastructure. You can inject mocked Intents, but not
- mocked Contexts. In general, you can't mock dependencies for the activity under test.
+ Most unit testing is specific to the Android component you are testing.
+ The topics <a href="{@docRoot}guide/topics/testing/activity_testing.html">Activity Testing</a>,
+ <a href="{@docRoot}guide/topics/testing/contentprovider_testing.html">
+ Content Provider Testing</a>, and <a href="{@docRoot}guide/topics/testing/service_testing.html">
+ Service Testing</a> each have a section entitled "What To Test" that lists possible testing
+ areas.
</p>
<p>
- In comparison, <code>ActivityUnitTestCase</code> is designed for unit testing, so it tests activities in an isolated system infrastructure. You can inject mocked or wrappered dependencies for
- the activity under test, particularly mocked Contexts. On the other hand, when you use this test case class the activity under test runs in isolation and can't interact with other activities.
+ When possible, you should run these tests on an actual device. If this is not possible, you can
+ use the <a href="{@docRoot}guide/developing/tools/emulator.html">Android Emulator</a> with
+ <a href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> configured for
+ the hardware, screens, and versions you want to test.
+</p>
+<h2 id="NextSteps">Next Steps</h2>
+<p>
+ To learn how to set up and run tests in Eclipse, please refer to <a
+ href="{@docRoot}guide/developing/testing/testing_eclipse.html">Testing in
+ Eclipse, with ADT</a>. If you're not working in Eclipse, refer to <a
+ href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other
+ IDEs</a>.
</p>
<p>
- As a rule of thumb, if you wanted to test an activity's interaction with the rest of Android, you would use <code>ActivityInstrumentationTestCase2</code>. If you wanted to do regression testing
- on an activity, you would use <code>ActivityUnitTestCase</code>.
+ If you want a step-by-step introduction to Android testing, try one of the
+ testing tutorials or sample test packages:
</p>
-<h3 id="Tests">Working with test methods</h3>
-<p>
- Each test case class provides methods that you use to set up the test environment and control the application under test. For example, all test case classes provide the JUnit {@link junit.framework.TestCase#setUp() setUp()}
- method that you can override to set up fixtures. In addition, you add methods to the class to define individual tests. Each method you add is run once each time you run the test application. If you override the <code>setUp()</code>
- method, it runs before each of your methods. Similarly, the JUnit {@link junit.framework.TestCase#tearDown() tearDown()} method is run once after each of your methods.
-</p>
-<p>
- The test case classes give you substantial control over starting and stopping components. For this reason, you have to specifically tell Android to start a component before you run tests against it. For example, you use the
- {@link android.test.ActivityInstrumentationTestCase2#getActivity()} method to start the activity under test. You can call this method once during the entire test case, or once for each test method. You can even destroy the
- activity under test by calling its {@link android.app.Activity#finish()} method and then restart it with <code>getActivity()</code> within a single test method.
-</p>
-<h3 id="RunTests">Running tests and seeing the results</h3>
-<p>
- To run your tests, you build your test project and then run the test application using the system utility Activity Manager with instrumentation. You provide to Activity Manager the name of the test runner (usually
- {@link android.test.InstrumentationTestRunner}) you specified for your application; the name includes both your test application's package name and the test runner class name. Activity Manager loads and starts your
- test application, kills any instances of the application under test, loads an instance of the application under test into the same process as the test application, and then passes control to the first test case
- class in your test application. The test runner then takes control of the tests, running each of your test methods against the application under test until all the methods in all the classes have been run.
-</p>
-<p>
- If you run a test within Eclipse with ADT, the output appears in a new JUnit view pane. If you run a test from the command line, the output goes to STDOUT.
-</p>
-<h2 id="TestAreas">What to Test</h2>
-<p>
- In addition to the functional areas you would normally test, here are some areas
- of Android application testing that you should consider:
-</p>
- <ul>
+<ul>
<li>
- Activity lifecycle events: You should test that your activities handle lifecycle events correctly. For example
- an activity should respond to pause or destroy events by saving its state. Remember that even a change in screen orientation
- causes the current activity to be destroyed, so you should test that accidental device movements don't accidentally lose the
- application state.
+ The <a
+ href="{@docRoot}resources/tutorials/testing/helloandroid_test.html">Hello,
+ Testing</a> tutorial introduces basic testing concepts and procedures in the
+ context of the Hello, World application.
</li>
<li>
- Database operations: You should ensure that database operations correctly handle changes to the application's state.
- To do this, use mock objects from the package {@link android.test.mock android.test.mock}.
+ The <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity
+ Testing</a> tutorial is an excellent follow-up to the Hello, Testing tutorial.
+ It guides you through a more complex testing scenario that you develop against a
+ more realistic application.
</li>
<li>
- Screen sizes and resolutions: Before you publish your application, make sure to test it on all of the
- screen sizes and densities on which you want it to run. You can test the application on multiple sizes and densities using
- AVDs, or you can test your application directly on the devices that you are targeting. For more information, see
- the topic <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.
+ The sample test package
+ <a href="{@docRoot}resources/samples/NotePadTest">Note Pad Test</a> is an example of
+ testing a {@link android.content.ContentProvider}. It contains a set of unit tests for the
+ Note Pad sample application's {@link android.content.ContentProvider}.
</li>
- </ul>
-<p>
- When possible, you should run these tests on an actual device. If this is not possible, you can
- use the <a href="{@docRoot}guide/developing/tools/emulator.html">Android Emulator</a> with
- <a href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> configured for
- the hardware, screens, and versions you want to test.
-</p>
-<h2 id="UITesting">Appendix: UI Testing Notes</h2>
-<p>
- The following sections have tips for testing the UI of your Android application, specifically
- to help you handle actions that run in the UI thread, touch screen and keyboard events, and home
- screen unlock during testing.
-</p>
-<h3 id="RunOnUIThread">Testing on the UI thread</h3>
-<p>
- An application's activities run on the application's <strong>UI thread</strong>. Once the
- UI is instantiated, for example in the activity's <code>onCreate()</code> method, then all
- interactions with the UI must run in the UI thread. When you run the application normally, it
- has access to the thread and does not have to do anything special.
-</p>
-<p>
- This changes when you run tests against the application. With instrumentation-based classes,
- you can invoke methods against the UI of the application under test. The other test classes don't allow this.
- To run an entire test method on the UI thread, you can annotate the thread with <code>@UIThreadTest</code>.
- Notice that this will run <em>all</em> of the method statements on the UI thread. Methods that do not interact with the UI
- are not allowed; for example, you can't invoke <code>Instrumentation.waitForIdleSync()</code>.
-</p>
-<p>
- To run a subset of a test method on the UI thread, create an anonymous class of type
- <code>Runnable</code>, put the statements you want in the <code>run()</code> method, and instantiate a new
- instance of the class as a parameter to the method <code><em>appActivity</em>.runOnUiThread()</code>, where
- <code><em>appActivity</em></code> is the instance of the app you are testing.
-</p>
-<p>
- For example, this code instantiates an activity to test, requests focus (a UI action) for the Spinner displayed
- by the activity, and then sends a key to it. Notice that the calls to <code>waitForIdleSync</code> and <code>sendKeys</code>
- aren't allowed to run on the UI thread:</p>
-<pre>
- private MyActivity mActivity; // MyActivity is the class name of the app under test
- private Spinner mSpinner;
-
- ...
-
- protected void setUp() throws Exception {
- super.setUp();
- mInstrumentation = getInstrumentation();
-
- mActivity = getActivity(); // get a references to the app under test
-
- /*
- * Get a reference to the main widget of the app under test, a Spinner
- */
- mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);
-
- ...
-
- public void aTest() {
- /*
- * request focus for the Spinner, so that the test can send key events to it
- * This request must be run on the UI thread. To do this, use the runOnUiThread method
- * and pass it a Runnable that contains a call to requestFocus on the Spinner.
- */
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- mSpinner.requestFocus();
- }
- });
-
- mInstrumentation.waitForIdleSync();
-
- this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-</pre>
-
-<h3 id="NotouchMode">Turning off touch mode</h3>
-<p>
- To control the emulator or a device with key events you send from your tests, you must turn off
- touch mode. If you do not do this, the key events are ignored.
-</p>
-<p>
- To turn off touch mode, you invoke <code>ActivityInstrumentationTestCase2.setActivityTouchMode(false)</code>
- <em>before</em> you call <code>getActivity()</code> to start the activity. You must invoke the method in a test method
- that is <em>not</em> running on the UI thread. For this reason, you can't invoke the touch mode method
- from a test method that is annotated with <code>@UIThread</code>. Instead, invoke the touch mode method from <code>setUp()</code>.
-</p>
-<h3 id="UnlockDevice">Unlocking the emulator or device</h3>
-<p>
- You may find that UI tests don't work if the emulator's or device's home screen is disabled with the keyguard pattern.
- This is because the application under test can't receive key events sent by <code>sendKeys()</code>. The best
- way to avoid this is to start your emulator or device first and then disable the keyguard for the home screen.
-</p>
-<p>
- You can also explicitly disable the keyguard. To do this,
- you need to add a permission in the manifest file (<code>AndroidManifest.xml</code>) and
- then disable the keyguard in your application under test. Note, though, that you either have to remove this before
- you publish your application, or you have to disable it programmatically in the published app.
-</p>
-<p>
- To add the the permission, add the element <code><uses-permission android:name="android.permission.DISABLE_KEYGUARD"/></code>
- as a child of the <code><manifest></code> element. To disable the KeyGuard, add the following code
- to the <code>onCreate()</code> method of activities you intend to test:
-</p>
-<pre>
- mKeyGuardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
- mLock = mKeyGuardManager.newKeyguardLock("<em>activity_classname</em>");
- mLock.disableKeyguard();
-</pre>
-<p>where <code><em>activity_classname</em></code> is the class name of the activity.</p>
-<h3 id="UITestTroubleshooting">Troubleshooting UI tests</h3>
-<p>
- This section lists some of the common test failures you may encounter in UI testing, and their causes:
-</p>
-<dl>
- <dt><code>WrongThreadException</code></dt>
- <dd>
- <p><strong>Problem:</strong></p>
- For a failed test, the Failure Trace contains the following error message:
- <code>
- android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
- </code>
- <p><strong>Probable Cause:</strong></p>
- This error is common if you tried to send UI events to the UI thread from outside the UI thread. This commonly happens if you send UI events
- from the test application, but you don't use the <code>@UIThread</code> annotation or the <code>runOnUiThread()</code> method. The test method tried to interact with the UI outside the UI thread.
- <p><strong>Suggested Resolution:</strong></p>
- Run the interaction on the UI thread. Use a test class that provides instrumentation. See the previous section <a href="#RunOnUIThread">Testing on the UI Thread</a>
- for more details.
- </dd>
- <dt><code>java.lang.RuntimeException</code></dt>
- <dd>
- <p><strong>Problem:</strong></p>
- For a failed test, the Failure Trace contains the following error message:
- <code>
- java.lang.RuntimeException: This method can not be called from the main application thread
- </code>
- <p><strong>Probable Cause:</strong></p>
- This error is common if your test method is annotated with <code>@UiThreadTest</code> but then tries to
- do something outside the UI thread or tries to invoke <code>runOnUiThread()</code>.
- <p><strong>Suggested Resolution:</strong></p>
- Remove the <code>@UiThreadTest</code> annotation, remove the <code>runOnUiThread()</code> call, or re-factor your tests.
- </dd>
-</dl>
+ <li>
+ The sample test package <a href="{@docRoot}resources/samples/AlarmServiceTest"}>
+ Alarm Service Test</a> is an example of testing a {@link android.app.Service}. It contains
+ a set of unit tests for the Alarm Service sample application's {@link android.app.Service}.
+ </li>
+</ul>
diff --git a/docs/html/guide/topics/testing/what_to_test.jd b/docs/html/guide/topics/testing/what_to_test.jd
new file mode 100644
index 0000000..e13538a
--- /dev/null
+++ b/docs/html/guide/topics/testing/what_to_test.jd
@@ -0,0 +1,84 @@
+page.title=What To Test
+@jd:body
+<p>
+ As you develop Android applications, knowing what to test is as important as knowing how to
+ test. This document lists some most common Android-related situations that you should consider
+ when you test, even at the unit test level. This is not an exhaustive list, and you consult the
+ documentation for the features that you use for more ideas. The
+ <a href="http://groups.google.com/group/android-developers">android-developers</a> Google Groups
+ site is another resource for information about testing.
+</p>
+<h2 id="Tests">Ideas for Testing</h2>
+<p>
+ The following sections are organized by behaviors or situations that you should test. Each
+ section contains a scenario that further illustrates the situation and the test or tests you
+ should do.
+</p>
+<h4>Change in orientation</h4>
+<p>
+ For devices that support multiple orientations, Android detects a change in orientation when
+ the user turns the device so that the display is "landscape" (long edge is horizontal) instead
+ of "portrait" (long edge is vertical).
+</p>
+<p>
+ When Android detects a change in orientation, its default behavior is to destroy and then
+ re-start the foreground Activity. You should consider testing the following:
+</p>
+<ul>
+ <li>
+ Is the screen re-drawn correctly? Any custom UI code you have should handle changes in the
+ orientation.
+ </li>
+ <li>
+ Does the application maintain its state? The Activity should not lose anything that the
+ user has already entered into the UI. The application should not "forget" its place in the
+ current transaction.
+ </li>
+</ul>
+<h4>Change in configuration</h4>
+<p>
+ A situation that is more general than a change in orientation is a change in the device's
+ configuration, such as a change in the availability of a keyboard or a change in system
+ language.
+</p>
+<p>
+ A change in configuration also triggers the default behavior of destroying and then restarting
+ the foreground Activity. Besides testing that the application maintains the UI and its
+ transaction state, you should also test that the application updates itself to respond
+ correctly to the new configuration.
+</p>
+<h4>Battery life</h4>
+<p>
+ Mobile devices primarily run on battery power. A device has finite "battery budget", and when it
+ is gone, the device is useless until it is recharged. You need to write your application to
+ minimize battery usage, you need to test its battery performance, and you need to test the
+ methods that manage battery usage.
+</p>
+<p>
+ Techniques for minimizing battery usage were presented at the 2010 Google I/O conference in the
+ presentation
+ <a href="http://code.google.com/events/io/2009/sessions/CodingLifeBatteryLife.html">
+ Coding for Life -- Battery Life, That Is</a>. This presentation describes the impact on battery
+ life of various operations, and the ways you can design your application to minimize these
+ impacts. When you code your application to reduce battery usage, you also write the
+ appropriate unit tests.
+</p>
+<h4>Dependence on external resources</h4>
+<p>
+ If your application depends on network access, SMS, Bluetooth, or GPS, then you should
+ test what happens when the resource or resources are not available.
+</p>
+<p>
+ For example, if your application uses the network,it can notify the user if access is
+ unavailable, or disable network-related features, or do both. For GPS, it can switch to
+ IP-based location awareness. It can also wait for WiFi access before doing large data transfers,
+ since WiFi transfers maximize battery usage compared to transfers over 3G or EDGE.
+</p>
+<p>
+ You can use the emulator to test network access and bandwidth. To learn more, please see
+ <a href="{@docRoot}guide/developing/tools/emulator.html#netspeed">Network Speed Emulation</a>.
+ To test GPS, you can use the emulator console and {@link android.location.LocationManager}. To
+ learn more about the emulator console, please see
+ <a href="{@docRoot}/guide/developing/tools/emulator.html#console">
+ Using the Emulator Console</a>.
+</p>
diff --git a/docs/html/images/testing/android_test_framework.png b/docs/html/images/testing/android_test_framework.png
index 6f80530..459975c 100755
--- a/docs/html/images/testing/android_test_framework.png
+++ b/docs/html/images/testing/android_test_framework.png
Binary files differ
diff --git a/docs/html/images/testing/eclipse_test_results.png b/docs/html/images/testing/eclipse_test_results.png
new file mode 100644
index 0000000..105e149
--- /dev/null
+++ b/docs/html/images/testing/eclipse_test_results.png
Binary files differ
diff --git a/docs/html/images/testing/eclipse_test_run_failure.png b/docs/html/images/testing/eclipse_test_run_failure.png
new file mode 100644
index 0000000..8111127
--- /dev/null
+++ b/docs/html/images/testing/eclipse_test_run_failure.png
Binary files differ
diff --git a/docs/html/images/testing/test_framework.png b/docs/html/images/testing/test_framework.png
new file mode 100644
index 0000000..fbc5fc2
--- /dev/null
+++ b/docs/html/images/testing/test_framework.png
Binary files differ
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index 436fc38..51f9aeb 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -61,9 +61,9 @@
virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
virtual void finishMediaUpdate() = 0;
virtual void mountObb(const String16& filename, const String16& key,
- const sp<IObbActionListener>& token) = 0;
+ const sp<IObbActionListener>& token, const int32_t nonce) = 0;
virtual void unmountObb(const String16& filename, const bool force,
- const sp<IObbActionListener>& token) = 0;
+ const sp<IObbActionListener>& token, const int32_t nonce) = 0;
virtual bool isObbMounted(const String16& filename) = 0;
virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
};
diff --git a/include/storage/IObbActionListener.h b/include/storage/IObbActionListener.h
index 1bedcc6..d78273c 100644
--- a/include/storage/IObbActionListener.h
+++ b/include/storage/IObbActionListener.h
@@ -29,7 +29,7 @@
public:
DECLARE_META_INTERFACE(ObbActionListener);
- virtual void onObbResult(const String16& filename, const String16& status) = 0;
+ virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 6533600..1a1821c 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -118,6 +118,8 @@
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t reqWidth, uint32_t reqHeight) = 0;
+ virtual status_t turnElectronBeamOff(int32_t mode) = 0;
+
/* Signal surfaceflinger that there might be some work to do
* This is an ASYNCHRONOUS call.
*/
@@ -142,7 +144,8 @@
FREEZE_DISPLAY,
UNFREEZE_DISPLAY,
SIGNAL,
- CAPTURE_SCREEN
+ CAPTURE_SCREEN,
+ TURN_ELECTRON_BEAM_OFF
};
virtual status_t onTransact( uint32_t code,
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 3ad9319..9ff6930 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -430,13 +430,14 @@
}
void mountObb(const String16& filename, const String16& key,
- const sp<IObbActionListener>& token)
+ const sp<IObbActionListener>& token, int32_t nonce)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
data.writeString16(filename);
data.writeString16(key);
data.writeStrongBinder(token->asBinder());
+ data.writeInt32(nonce);
if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
LOGD("mountObb could not contact remote\n");
return;
@@ -448,12 +449,15 @@
}
}
- void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token)
+ void unmountObb(const String16& filename, const bool force,
+ const sp<IObbActionListener>& token, const int32_t nonce)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
data.writeString16(filename);
data.writeInt32(force ? 1 : 0);
+ data.writeStrongBinder(token->asBinder());
+ data.writeInt32(nonce);
if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
LOGD("unmountObb could not contact remote\n");
return;
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
index 5bfece7..eaa211e 100644
--- a/libs/storage/IObbActionListener.cpp
+++ b/libs/storage/IObbActionListener.cpp
@@ -30,7 +30,7 @@
: BpInterface<IObbActionListener>(impl)
{ }
- virtual void onObbResult(const String16& filename, const String16& status) { }
+ virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { }
};
IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
@@ -44,8 +44,9 @@
case TRANSACTION_onObbResult: {
CHECK_INTERFACE(IObbActionListener, data, reply);
String16 filename = data.readString16();
- String16 state = data.readString16();
- onObbResult(filename, state);
+ int32_t nonce = data.readInt32();
+ int32_t state = data.readInt32();
+ onObbResult(filename, nonce, state);
reply->writeNoException();
return NO_ERROR;
} break;
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index d676f5e..d72561f 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -142,6 +142,15 @@
return reply.readInt32();
}
+ virtual status_t turnElectronBeamOff(int32_t mode)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeInt32(mode);
+ remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_OFF, data, &reply);
+ return reply.readInt32();
+ }
+
virtual void signal() const
{
Parcel data, reply;
@@ -224,6 +233,12 @@
reply->writeInt32(f);
reply->writeInt32(res);
} break;
+ case TURN_ELECTRON_BEAM_OFF: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t mode = data.readInt32();
+ status_t res = turnElectronBeamOff(mode);
+ reply->writeInt32(res);
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 41b6ff3..6ba19d7 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -432,10 +432,9 @@
switch (dropReason) {
case DROP_REASON_POLICY:
#if DEBUG_INBOUND_EVENT_DETAILS
- LOGD("Dropped event because policy requested that it not be delivered to the application.");
+ LOGD("Dropped event because policy consumed it.");
#endif
- reason = "inbound event was dropped because the policy requested that it not be "
- "delivered to the application";
+ reason = "inbound event was dropped because the policy consumed it";
break;
case DROP_REASON_DISABLED:
LOGI("Dropped event because input dispatch is disabled.");
@@ -625,15 +624,13 @@
if (*dropReason == DROP_REASON_NOT_DROPPED) {
*dropReason = DROP_REASON_POLICY;
}
- resetTargetsLocked();
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_SUCCEEDED);
- return true;
}
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
resetTargetsLocked();
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true;
}
@@ -713,7 +710,8 @@
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
resetTargetsLocked();
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true;
}
@@ -3320,7 +3318,7 @@
}
}
- for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ for (size_t i = 0; i < mMotionMementos.size(); ) {
const MotionMemento& memento = mMotionMementos.itemAt(i);
if (shouldCancelEvent(memento.source, options)) {
outEvents.push(allocator->obtainMotionEntry(currentTime,
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 2f20641..a4233e7 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -21,10 +21,13 @@
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
+#include <utils/Atomic.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
using namespace android;
@@ -38,20 +41,46 @@
mStorageManager(mgr)
{}
- virtual void onObbResult(const android::String16& filename, const android::String16& state);
+ virtual void onObbResult(const android::String16& filename, const int32_t nonce,
+ const int32_t state);
+};
+
+class ObbCallback {
+public:
+ ObbCallback(int32_t _nonce, AStorageManager_obbCallbackFunc _cb, void* _data)
+ : nonce(_nonce)
+ , cb(_cb)
+ , data(_data)
+ {}
+
+ int32_t nonce;
+ AStorageManager_obbCallbackFunc cb;
+ void* data;
};
struct AStorageManager : public RefBase {
protected:
- AStorageManager_obbCallbackFunc mObbCallback;
- void* mObbCallbackData;
+ Mutex mCallbackLock;
+ Vector<ObbCallback*> mCallbacks;
+ volatile int32_t mNextNonce;
sp<ObbActionListener> mObbActionListener;
sp<IMountService> mMountService;
+ int32_t getNextNonce() {
+ return android_atomic_inc(&mNextNonce);
+ }
+
+ ObbCallback* registerObbCallback(AStorageManager_obbCallbackFunc func, void* data) {
+ ObbCallback* cb = new ObbCallback(getNextNonce(), func, data);
+ {
+ AutoMutex _l(mCallbackLock);
+ mCallbacks.push(cb);
+ }
+ return cb;
+ }
+
public:
AStorageManager()
- : mObbCallback(NULL)
- , mObbCallbackData(NULL)
{
}
@@ -73,26 +102,40 @@
return true;
}
- void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
- mObbCallback = cb;
- mObbCallbackData = data;
- }
+ void fireCallback(const char* filename, const int32_t nonce, const int32_t state) {
+ ObbCallback* target = NULL;
+ {
+ AutoMutex _l(mCallbackLock);
+ int N = mCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ ObbCallback* cb = mCallbacks.editItemAt(i);
+ if (cb->nonce == nonce) {
+ target = cb;
+ mCallbacks.removeAt(i);
+ break;
+ }
+ }
+ }
- void fireCallback(const char* filename, const char* state) {
- if (mObbCallback != NULL) {
- mObbCallback(filename, state, mObbCallbackData);
+ if (target != NULL) {
+ target->cb(filename, state, target->data);
+ delete target;
+ } else {
+ LOGI("Didn't find the callback handler for: %s\n", filename);
}
}
- void mountObb(const char* filename, const char* key) {
+ void mountObb(const char* filename, const char* key, AStorageManager_obbCallbackFunc func, void* data) {
+ ObbCallback* cb = registerObbCallback(func, data);
String16 filename16(filename);
String16 key16(key);
- mMountService->mountObb(filename16, key16, mObbActionListener);
+ mMountService->mountObb(filename16, key16, mObbActionListener, cb->nonce);
}
- void unmountObb(const char* filename, const bool force) {
+ void unmountObb(const char* filename, const bool force, AStorageManager_obbCallbackFunc func, void* data) {
+ ObbCallback* cb = registerObbCallback(func, data);
String16 filename16(filename);
- mMountService->unmountObb(filename16, force, mObbActionListener);
+ mMountService->unmountObb(filename16, force, mObbActionListener, cb->nonce);
}
int isObbMounted(const char* filename) {
@@ -111,8 +154,8 @@
}
};
-void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) {
- mStorageManager->fireCallback(String8(filename).string(), String8(state).string());
+void ObbActionListener::onObbResult(const android::String16& filename, const int32_t nonce, const int32_t state) {
+ mStorageManager->fireCallback(String8(filename).string(), nonce, state);
}
@@ -131,16 +174,14 @@
}
}
-void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) {
- mgr->setObbCallback(cb, data);
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key,
+ AStorageManager_obbCallbackFunc cb, void* data) {
+ mgr->mountObb(filename, key, cb, data);
}
-void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
- mgr->mountObb(filename, key);
-}
-
-void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force) {
- mgr->unmountObb(filename, force != 0);
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force,
+ AStorageManager_obbCallbackFunc cb, void* data) {
+ mgr->unmountObb(filename, force != 0, cb, data);
}
int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename) {
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
index 6f925c1..c202693 100644
--- a/native/include/android/storage_manager.h
+++ b/native/include/android/storage_manager.h
@@ -18,6 +18,8 @@
#ifndef ANDROID_STORAGE_MANAGER_H
#define ANDROID_STORAGE_MANAGER_H
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -25,6 +27,60 @@
struct AStorageManager;
typedef struct AStorageManager AStorageManager;
+enum {
+ /*
+ * The OBB container is now mounted and ready for use. Can be returned
+ * as the status for callbacks made during asynchronous OBB actions.
+ */
+ AOBB_STATE_MOUNTED = 1,
+
+ /*
+ * The OBB container is now unmounted and not usable. Can be returned
+ * as the status for callbacks made during asynchronous OBB actions.
+ */
+ AOBB_STATE_UNMOUNTED = 2,
+
+ /*
+ * There was an internal system error encountered while trying to
+ * mount the OBB. Can be returned as the status for callbacks made
+ * during asynchronous OBB actions.
+ */
+ AOBB_STATE_ERROR_INTERNAL = 20,
+
+ /*
+ * The OBB could not be mounted by the system. Can be returned as the
+ * status for callbacks made during asynchronous OBB actions.
+ */
+ AOBB_STATE_ERROR_COULD_NOT_MOUNT = 21,
+
+ /*
+ * The OBB could not be unmounted. This most likely indicates that a
+ * file is in use on the OBB. Can be returned as the status for
+ * callbacks made during asynchronous OBB actions.
+ */
+ AOBB_STATE_ERROR_COULD_NOT_UNMOUNT = 22,
+
+ /*
+ * A call was made to unmount the OBB when it was not mounted. Can be
+ * returned as the status for callbacks made during asynchronous OBB
+ * actions.
+ */
+ AOBB_STATE_ERROR_NOT_MOUNTED = 23,
+
+ /*
+ * The OBB has already been mounted. Can be returned as the status for
+ * callbacks made during asynchronous OBB actions.
+ */
+ AOBB_STATE_ERROR_ALREADY_MOUNTED = 24,
+
+ /*
+ * The current application does not have permission to use this OBB
+ * because the OBB indicates it's owned by a different package or the
+ * key used to open it is incorrect. Can be returned as the status for
+ * callbacks made during asynchronous OBB actions.
+ */
+ AOBB_STATE_ERROR_PERMISSION_DENIED = 25,
+};
/**
* Obtains a new instance of AStorageManager.
@@ -39,22 +95,19 @@
/**
* Callback function for asynchronous calls made on OBB files.
*/
-typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data);
-
-/**
- * Callback to call when requested asynchronous OBB operation is complete.
- */
-void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data);
+typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int32_t state, void* data);
/**
* Attempts to mount an OBB file. This is an asynchronous operation.
*/
-void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
+void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key,
+ AStorageManager_obbCallbackFunc cb, void* data);
/**
* Attempts to unmount an OBB file. This is an asynchronous operation.
*/
-void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
+void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force,
+ AStorageManager_obbCallbackFunc cb, void* data);
/**
* Check whether an OBB is mounted.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0fccbe7..7995869 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -20,6 +20,10 @@
<activity android:name=".usb.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
+ <activity android:name="com.android.internal.app.ExternalMediaFormatActivity"
+ android:theme="@*android:style/Theme.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
</application>
</manifest>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index b9e915a4..47ed7da 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -16,33 +16,17 @@
package com.android.systemui.usb;
-import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.os.Bundle;
import android.os.Environment;
-import android.os.Handler;
-import android.os.storage.IMountService;
-import android.os.Message;
-import android.os.ServiceManager;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
-import android.os.storage.StorageResultCode;
import android.provider.Settings;
import android.util.Slog;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
public class StorageNotification extends StorageEventListener {
private static final String TAG = "StorageNotification";
@@ -165,10 +149,16 @@
* Show safe to unmount media notification, and enable UMS
* notification if connected.
*/
- setMediaStorageNotification(
- com.android.internal.R.string.ext_media_safe_unmount_notification_title,
- com.android.internal.R.string.ext_media_safe_unmount_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
+ if (Environment.isExternalStorageRemovable()) {
+ setMediaStorageNotification(
+ com.android.internal.R.string.ext_media_safe_unmount_notification_title,
+ com.android.internal.R.string.ext_media_safe_unmount_notification_message,
+ com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
+ } else {
+ // This device does not have removable storage, so
+ // don't tell the user they can remove it.
+ setMediaStorageNotification(0, 0, 0, false, false, null);
+ }
updateUsbMassStorageNotification(mUmsAvailable);
}
} else {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index d9bceec..3cf4360 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1057,10 +1057,6 @@
@Override
public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
int keyCode, int metaState, int repeatCount, int policyFlags) {
- if ((policyFlags & WindowManagerPolicy.FLAG_TRUSTED) == 0) {
- return false;
- }
-
final boolean keyguardOn = keyguardOn();
final boolean down = (action == KeyEvent.ACTION_DOWN);
final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0);
@@ -1739,9 +1735,6 @@
public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
int policyFlags, boolean isScreenOn) {
int result = ACTION_PASS_TO_USER;
- if ((policyFlags & WindowManagerPolicy.FLAG_TRUSTED) == 0) {
- return result;
- }
if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) {
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
@@ -1749,7 +1742,14 @@
final boolean isWakeKey = (policyFlags
& (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
-
+
+ // If the key is injected, pretend that the screen is on and don't let the
+ // device go to sleep. This feature is mainly used for testing purposes.
+ final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
+ if (isInjected) {
+ isScreenOn = true;
+ }
+
// If screen is off then we treat the case where the keyguard is open but hidden
// the same as if it were open and in front.
// This will prevent any keys other than the power button from waking the screen
@@ -1848,7 +1848,7 @@
|| (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) {
mShouldTurnOffOnKeyUp = false;
} else {
- // only try to turn off the screen if we didn't already hang up
+ // Only try to turn off the screen if we didn't already hang up.
mShouldTurnOffOnKeyUp = true;
mHandler.postDelayed(mPowerLongPress,
ViewConfiguration.getGlobalActionKeyTimeout());
@@ -1871,12 +1871,14 @@
if (keyguardActive
|| (sleeps && !gohome)
|| (gohome && !goHome() && sleeps)) {
- // they must already be on the keyguad or home screen,
- // go to sleep instead
- Log.d(TAG, "I'm tired mEndcallBehavior=0x"
- + Integer.toHexString(mEndcallBehavior));
- result &= ~ACTION_POKE_USER_ACTIVITY;
- result |= ACTION_GO_TO_SLEEP;
+ // They must already be on the keyguard or home screen,
+ // go to sleep instead unless the event was injected.
+ if (!isInjected) {
+ Log.d(TAG, "I'm tired mEndcallBehavior=0x"
+ + Integer.toHexString(mEndcallBehavior));
+ result &= ~ACTION_POKE_USER_ACTIVITY;
+ result |= ACTION_GO_TO_SLEEP;
+ }
}
result &= ~ACTION_PASS_TO_USER;
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 3b2d836..775f5c8 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -44,6 +44,7 @@
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
import android.os.storage.IObbActionListener;
+import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageResultCode;
import android.security.MessageDigest;
import android.util.Slog;
@@ -53,7 +54,6 @@
import java.io.PrintWriter;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -161,25 +161,25 @@
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
class ObbState implements IBinder.DeathRecipient {
- public ObbState(String filename, IObbActionListener token, int callerUid)
+ public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
throws RemoteException {
this.filename = filename;
- this.token = token;
this.callerUid = callerUid;
- mounted = false;
+ this.token = token;
+ this.nonce = nonce;
}
// OBB source filename
- final String filename;
-
- // Token of remote Binder caller
- final IObbActionListener token;
+ String filename;
// Binder.callingUid()
final public int callerUid;
- // Whether this is mounted currently.
- boolean mounted;
+ // Token of remote Binder caller
+ final IObbActionListener token;
+
+ // Identifier to pass back to the token
+ final int nonce;
public IBinder getBinder() {
return token.asBinder();
@@ -208,8 +208,6 @@
sb.append(token.toString());
sb.append(",callerUid=");
sb.append(callerUid);
- sb.append(",mounted=");
- sb.append(mounted);
sb.append('}');
return sb.toString();
}
@@ -223,6 +221,7 @@
private static final int OBB_MCS_BOUND = 2;
private static final int OBB_MCS_UNBIND = 3;
private static final int OBB_MCS_RECONNECT = 4;
+ private static final int OBB_FLUSH_MOUNT_STATE = 5;
/*
* Default Container Service information
@@ -500,40 +499,23 @@
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
return;
}
- // Update state on PackageManager
+
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+ // Tell the package manager the media is gone.
mPms.updateExternalMediaStatus(false, false);
+
+ /*
+ * Some OBBs might have been unmounted when this volume was
+ * unmounted, so send a message to the handler to let it know to
+ * remove those from the list of mounted OBBS.
+ */
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
+ path));
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
+ // Tell the package manager the media is available for use.
mPms.updateExternalMediaStatus(true, false);
}
- // Remove all OBB mappings and listeners from this path
- synchronized (mObbMounts) {
- final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
-
- final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
- while (i.hasNext()) {
- final Entry<String, ObbState> obbEntry = i.next();
-
- // If this entry's source file is in the volume path that got
- // unmounted, remove it because it's no longer valid.
- if (obbEntry.getKey().startsWith(path)) {
- obbStatesToRemove.add(obbEntry.getValue());
- }
- }
-
- for (final ObbState obbState : obbStatesToRemove) {
- removeObbState(obbState);
-
- try {
- obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
- } catch (RemoteException e) {
- Slog.i(TAG, "Couldn't send unmount notification for OBB: "
- + obbState.filename);
- }
- }
- }
-
String oldState = mLegacyState;
mLegacyState = state;
@@ -1530,6 +1512,10 @@
}
public String getMountedObbPath(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
+ }
+
waitForReady();
warnOnNotMounted();
@@ -1552,164 +1538,98 @@
}
public boolean isObbMounted(String filename) {
- synchronized (mObbMounts) {
- final ObbState obbState = mObbPathToStateMap.get(filename);
- if (obbState != null) {
- synchronized (obbState) {
- return obbState.mounted;
- }
- }
+ if (filename == null) {
+ throw new IllegalArgumentException("filename cannot be null");
}
- return false;
+
+ synchronized (mObbMounts) {
+ return mObbPathToStateMap.containsKey(filename);
+ }
}
- public void mountObb(String filename, String key, IObbActionListener token)
+ public void mountObb(String filename, String key, IObbActionListener token, int nonce)
throws RemoteException {
- waitForReady();
- warnOnNotMounted();
-
if (filename == null) {
throw new IllegalArgumentException("filename cannot be null");
- } else if (token == null) {
+ }
+
+ if (token == null) {
throw new IllegalArgumentException("token cannot be null");
}
- final ObbState obbState;
-
- synchronized (mObbMounts) {
- if (isObbMounted(filename)) {
- try {
- token.onObbResult(filename, Environment.MEDIA_MOUNTED);
- } catch (RemoteException e) {
- Slog.d(TAG, "Could not send unmount notification for: " + filename);
- }
- return;
- }
-
- final int callerUid = Binder.getCallingUid();
- obbState = new ObbState(filename, token, callerUid);
- addObbState(obbState);
- }
-
- String hashedKey = null;
- if (key != null) {
- final MessageDigest md;
- try {
- md = MessageDigest.getInstance("MD5");
- } catch (NoSuchAlgorithmException e) {
- Slog.e(TAG, "Could not load MD5 algorithm", e);
- try {
- token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
- } catch (RemoteException e1) {
- Slog.d(TAG, "Could not send unmount notification for: " + filename);
- }
- return;
- }
-
- hashedKey = HexDump.toHexString(md.digest(key.getBytes()));
- }
-
- ObbAction action = new MountObbAction(obbState, hashedKey);
+ final int callerUid = Binder.getCallingUid();
+ final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
+ final ObbAction action = new MountObbAction(obbState, key);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
- public void unmountObb(String filename, boolean force, IObbActionListener token) {
+ public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
+ throws RemoteException {
if (filename == null) {
throw new IllegalArgumentException("filename cannot be null");
- } else if (token == null) {
- throw new IllegalArgumentException("token cannot be null");
}
- final ObbState obbState;
-
- synchronized (mObbMounts) {
- if (!isObbMounted(filename)) {
- try {
- token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
- } catch (RemoteException e) {
- Slog.d(TAG, "Could not send unmount notification for: " + filename);
- }
- return;
- }
-
- obbState = mObbPathToStateMap.get(filename);
-
- if (Binder.getCallingUid() != obbState.callerUid) {
- throw new SecurityException("caller UID does not match original mount caller UID");
- } else if (!token.asBinder().equals(obbState.getBinder())) {
- throw new SecurityException("caller does not match original mount caller");
- }
- }
-
- ObbAction action = new UnmountObbAction(obbState, force);
+ final int callerUid = Binder.getCallingUid();
+ final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
+ final ObbAction action = new UnmountObbAction(obbState, force);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
- private void addObbState(ObbState obbState) throws RemoteException {
- synchronized (mObbMounts) {
- final IBinder binder = obbState.getBinder();
- List<ObbState> obbStates = mObbMounts.get(binder);
- final boolean unique;
+ private void addObbStateLocked(ObbState obbState) throws RemoteException {
+ final IBinder binder = obbState.getBinder();
+ List<ObbState> obbStates = mObbMounts.get(binder);
- if (obbStates == null) {
- obbStates = new ArrayList<ObbState>();
- mObbMounts.put(binder, obbStates);
- unique = true;
- } else {
- unique = obbStates.contains(obbState);
- }
-
- if (unique) {
- obbStates.add(obbState);
- try {
- obbState.link();
- } catch (RemoteException e) {
- /*
- * The binder died before we could link it, so clean up our
- * state and return failure.
- */
- obbStates.remove(obbState);
- if (obbStates.isEmpty()) {
- mObbMounts.remove(binder);
- }
-
- // Rethrow the error so mountObb can get it
- throw e;
+ if (obbStates == null) {
+ obbStates = new ArrayList<ObbState>();
+ mObbMounts.put(binder, obbStates);
+ } else {
+ for (final ObbState o : obbStates) {
+ if (o.filename.equals(obbState.filename)) {
+ throw new IllegalStateException("Attempt to add ObbState twice. "
+ + "This indicates an error in the MountService logic.");
}
}
-
- mObbPathToStateMap.put(obbState.filename, obbState);
}
+
+ obbStates.add(obbState);
+ try {
+ obbState.link();
+ } catch (RemoteException e) {
+ /*
+ * The binder died before we could link it, so clean up our state
+ * and return failure.
+ */
+ obbStates.remove(obbState);
+ if (obbStates.isEmpty()) {
+ mObbMounts.remove(binder);
+ }
+
+ // Rethrow the error so mountObb can get it
+ throw e;
+ }
+
+ mObbPathToStateMap.put(obbState.filename, obbState);
}
- private void removeObbState(ObbState obbState) {
- synchronized (mObbMounts) {
- final IBinder binder = obbState.getBinder();
- final List<ObbState> obbStates = mObbMounts.get(binder);
- if (obbStates != null) {
- if (obbStates.remove(obbState)) {
- obbState.unlink();
- }
- if (obbStates.isEmpty()) {
- mObbMounts.remove(binder);
- }
+ private void removeObbStateLocked(ObbState obbState) {
+ final IBinder binder = obbState.getBinder();
+ final List<ObbState> obbStates = mObbMounts.get(binder);
+ if (obbStates != null) {
+ if (obbStates.remove(obbState)) {
+ obbState.unlink();
}
-
- mObbPathToStateMap.remove(obbState.filename);
+ if (obbStates.isEmpty()) {
+ mObbMounts.remove(binder);
+ }
}
- }
- private void replaceObbState(ObbState oldObbState, ObbState newObbState) throws RemoteException {
- synchronized (mObbMounts) {
- removeObbState(oldObbState);
- addObbState(newObbState);
- }
+ mObbPathToStateMap.remove(obbState.filename);
}
private class ObbActionHandler extends Handler {
@@ -1808,6 +1728,47 @@
}
break;
}
+ case OBB_FLUSH_MOUNT_STATE: {
+ final String path = (String) msg.obj;
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Flushing all OBB state for path " + path);
+
+ synchronized (mObbMounts) {
+ final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
+
+ final Iterator<Entry<String, ObbState>> i =
+ mObbPathToStateMap.entrySet().iterator();
+ while (i.hasNext()) {
+ final Entry<String, ObbState> obbEntry = i.next();
+
+ /*
+ * If this entry's source file is in the volume path
+ * that got unmounted, remove it because it's no
+ * longer valid.
+ */
+ if (obbEntry.getKey().startsWith(path)) {
+ obbStatesToRemove.add(obbEntry.getValue());
+ }
+ }
+
+ for (final ObbState obbState : obbStatesToRemove) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Removing state for " + obbState.filename);
+
+ removeObbStateLocked(obbState);
+
+ try {
+ obbState.token.onObbResult(obbState.filename, obbState.nonce,
+ OnObbStateChangeListener.UNMOUNTED);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Couldn't send unmount notification for OBB: "
+ + obbState.filename);
+ }
+ }
+ }
+ break;
+ }
}
}
@@ -1886,9 +1847,13 @@
return obbInfo;
}
- protected void sendNewStatusOrIgnore(String filename, String status) {
+ protected void sendNewStatusOrIgnore(int status) {
+ if (mObbState == null || mObbState.token == null) {
+ return;
+ }
+
try {
- mObbState.token.onObbResult(filename, status);
+ mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
@@ -1904,89 +1869,80 @@
}
public void handleExecute() throws IOException, RemoteException {
+ waitForReady();
+ warnOnNotMounted();
+
final ObbInfo obbInfo = getObbInfo();
- /*
- * If someone tried to trick us with some weird characters, rectify
- * it here.
- */
- if (!mObbState.filename.equals(obbInfo.filename)) {
- if (DEBUG_OBB)
- Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
- + obbInfo.filename);
-
- synchronized (mObbMounts) {
- /*
- * If the real filename is already mounted, discard this
- * state and notify the caller that the OBB is already
- * mounted.
- */
- if (isObbMounted(obbInfo.filename)) {
- if (DEBUG_OBB)
- Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);
-
- removeObbState(mObbState);
- sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
- return;
- }
-
- /*
- * It's not already mounted, so we have to replace the state
- * with the state containing the actual filename.
- */
- ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
- mObbState.callerUid);
- replaceObbState(mObbState, newObbState);
- mObbState = newObbState;
- }
- }
-
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
+ Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
+ + " which is owned by " + obbInfo.packageName);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ return;
}
- boolean mounted = false;
- int rc;
- synchronized (mObbState) {
- if (mObbState.mounted) {
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+ final boolean isMounted;
+ synchronized (mObbMounts) {
+ isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
+ }
+ if (isMounted) {
+ Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
+ return;
+ }
+
+ /*
+ * The filename passed in might not be the canonical name, so just
+ * set the filename to the canonicalized version.
+ */
+ mObbState.filename = obbInfo.filename;
+
+ final String hashedKey;
+ if (mKey == null) {
+ hashedKey = "none";
+ } else {
+ final MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(TAG, "Could not load MD5 algorithm", e);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
return;
}
- rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb mount %s %s %d", mObbState.filename,
- mKey != null ? mKey : "none",
- mObbState.callerUid);
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code != VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- }
+ hashedKey = HexDump.toHexString(md.digest(mKey.getBytes()));
+ }
- if (rc == StorageResultCode.OperationSucceeded) {
- mObbState.mounted = mounted = true;
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
+ mObbState.callerUid);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
}
}
- if (mounted) {
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+ if (rc == StorageResultCode.OperationSucceeded) {
+ if (DEBUG_OBB)
+ Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
+
+ synchronized (mObbMounts) {
+ addObbStateLocked(mObbState);
+ }
+
+ sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
} else {
Slog.e(TAG, "Couldn't mount OBB file: " + rc);
- // We didn't succeed, so remove this from the mount-set.
- removeObbState(mObbState);
-
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
}
}
public void handleError() {
- removeObbState(mObbState);
-
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
}
@Override
@@ -1999,6 +1955,8 @@
sb.append(mObbState.callerUid);
sb.append(",token=");
sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+ sb.append(",binder=");
+ sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
sb.append('}');
return sb.toString();
}
@@ -2013,68 +1971,61 @@
}
public void handleExecute() throws IOException {
+ waitForReady();
+ warnOnNotMounted();
+
final ObbInfo obbInfo = getObbInfo();
- /*
- * If someone tried to trick us with some weird characters, rectify
- * it here.
- */
+ final ObbState obbState;
synchronized (mObbMounts) {
- if (!isObbMounted(obbInfo.filename)) {
- removeObbState(mObbState);
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
- return;
- }
+ obbState = mObbPathToStateMap.get(obbInfo.filename);
+ }
- if (!mObbState.filename.equals(obbInfo.filename)) {
- removeObbState(mObbState);
- mObbState = mObbPathToStateMap.get(obbInfo.filename);
+ if (obbState == null) {
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
+ return;
+ }
+
+ if (obbState.callerUid != mObbState.callerUid) {
+ Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
+ + " (owned by " + obbInfo.packageName + ")");
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ return;
+ }
+
+ mObbState.filename = obbInfo.filename;
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", mObbState.filename,
+ (mForceUnmount ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ // If it's not mounted then we've already won.
+ rc = StorageResultCode.OperationSucceeded;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
}
}
- boolean unmounted = false;
- synchronized (mObbState) {
- if (!mObbState.mounted) {
- sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
- return;
+ if (rc == StorageResultCode.OperationSucceeded) {
+ synchronized (mObbMounts) {
+ removeObbStateLocked(obbState);
}
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb unmount %s%s", mObbState.filename,
- (mForceUnmount ? " force" : ""));
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
- } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
- // If it's not mounted then we've already won.
- rc = StorageResultCode.OperationSucceeded;
- } else {
- rc = StorageResultCode.OperationFailedInternalError;
- }
- }
-
- if (rc == StorageResultCode.OperationSucceeded) {
- mObbState.mounted = false;
- unmounted = true;
- }
- }
-
- if (unmounted) {
- removeObbState(mObbState);
-
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
} else {
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+ Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
}
}
public void handleError() {
- removeObbState(mObbState);
-
- sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
+ sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
}
@Override
@@ -2090,7 +2041,7 @@
sb.append(",token=");
sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
sb.append(",binder=");
- sb.append(mObbState.getBinder().toString());
+ sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
sb.append('}');
return sb.toString();
}
@@ -2105,16 +2056,27 @@
return;
}
- pw.println(" mObbMounts:");
-
synchronized (mObbMounts) {
- final Collection<List<ObbState>> obbStateLists = mObbMounts.values();
+ pw.println(" mObbMounts:");
- for (final List<ObbState> obbStates : obbStateLists) {
+ final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
+ while (binders.hasNext()) {
+ Entry<IBinder, List<ObbState>> e = binders.next();
+ pw.print(" Key="); pw.println(e.getKey().toString());
+ final List<ObbState> obbStates = e.getValue();
for (final ObbState obbState : obbStates) {
- pw.print(" "); pw.println(obbState.toString());
+ pw.print(" "); pw.println(obbState.toString());
}
}
+
+ pw.println("");
+ pw.println(" mObbPathToStateMap:");
+ final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
+ while (maps.hasNext()) {
+ final Entry<String, ObbState> e = maps.next();
+ pw.print(" "); pw.print(e.getKey());
+ pw.print(" -> "); pw.println(e.getValue().toString());
+ }
}
}
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index bc802a8..1df583a 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -7208,6 +7208,8 @@
pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
pw.print(" installStatus="); pw.print(ps.installStatus);
pw.print(" enabled="); pw.println(ps.enabled);
+ pw.print(" versionCode="); pw.print(ps.versionCode);
+ pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
if (ps.disabledComponents.size() > 0) {
pw.println(" disabledComponents:");
for (String s : ps.disabledComponents) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 88a4c90..f53ce2d 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -141,9 +141,7 @@
// used for noChangeLights in setPowerState()
private static final int LIGHTS_MASK = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT;
- static final boolean ANIMATE_SCREEN_LIGHTS = true;
- static final boolean ANIMATE_BUTTON_LIGHTS = false;
- static final boolean ANIMATE_KEYBOARD_LIGHTS = false;
+ boolean mAnimateScreenLights = true;
static final int ANIM_STEPS = 60/4;
// Slower animation for autobrightness changes
@@ -201,15 +199,12 @@
private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
private UnsynchronizedWakeLock mProximityPartialLock;
private HandlerThread mHandlerThread;
+ private HandlerThread mScreenOffThread;
+ private Handler mScreenOffHandler;
private Handler mHandler;
private final TimeoutTask mTimeoutTask = new TimeoutTask();
- private final LightAnimator mLightAnimator = new LightAnimator();
private final BrightnessState mScreenBrightness
= new BrightnessState(SCREEN_BRIGHT_BIT);
- private final BrightnessState mKeyboardBrightness
- = new BrightnessState(KEYBOARD_BRIGHT_BIT);
- private final BrightnessState mButtonBrightness
- = new BrightnessState(BUTTON_BRIGHT_BIT);
private boolean mStillNeedSleepNotification;
private boolean mIsPowered = false;
private IActivityManager mActivityService;
@@ -261,6 +256,7 @@
private native void nativeInit();
private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
+ private native void nativeStartSurfaceFlingerAnimation();
/*
static PrintStream mLog;
@@ -485,6 +481,35 @@
mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
+ nativeInit();
+ synchronized (mLocks) {
+ updateNativePowerStateLocked();
+ }
+
+ mInitComplete = false;
+ mScreenOffThread = new HandlerThread("PowerManagerService.mScreenOffThread") {
+ @Override
+ protected void onLooperPrepared() {
+ mScreenOffHandler = new Handler();
+ synchronized (mScreenOffThread) {
+ mInitComplete = true;
+ mScreenOffThread.notifyAll();
+ }
+ }
+ };
+ mScreenOffThread.start();
+
+ synchronized (mScreenOffThread) {
+ while (!mInitComplete) {
+ try {
+ mScreenOffThread.wait();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+ mInitComplete = false;
mHandlerThread = new HandlerThread("PowerManagerService") {
@Override
protected void onLooperPrepared() {
@@ -531,6 +556,9 @@
Resources resources = mContext.getResources();
+ mAnimateScreenLights = resources.getBoolean(
+ com.android.internal.R.bool.config_animateScreenLights);
+
mUnplugTurnsOnScreen = resources.getBoolean(
com.android.internal.R.bool.config_unplugTurnsOnScreen);
@@ -1093,8 +1121,6 @@
pw.println(" mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
pw.println(" mAutoBrightessEnabled=" + mAutoBrightessEnabled);
mScreenBrightness.dump(pw, " mScreenBrightness: ");
- mKeyboardBrightness.dump(pw, " mKeyboardBrightness: ");
- mButtonBrightness.dump(pw, " mButtonBrightness: ");
int N = mLocks.size();
pw.println();
@@ -1724,7 +1750,8 @@
// I don't think we need to check the current state here because all of these
// Power.setScreenState and sendNotificationLocked can both handle being
// called multiple times in the same state. -joeo
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime, mTouchCycles);
+ EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, mTotalTouchDownTime,
+ mTouchCycles);
mLastTouchDown = 0;
int err = setScreenStateLocked(false);
if (err == 0) {
@@ -1754,145 +1781,95 @@
int onMask = 0;
int preferredBrightness = getPreferredBrightness();
- boolean startAnimation = false;
if ((difference & KEYBOARD_BRIGHT_BIT) != 0) {
- if (ANIMATE_KEYBOARD_LIGHTS) {
- if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
- mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
- ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
- Power.BRIGHTNESS_ON);
- } else {
- mKeyboardBrightness.setTargetLocked(Power.BRIGHTNESS_ON,
- ANIM_STEPS, INITIAL_KEYBOARD_BRIGHTNESS,
- Power.BRIGHTNESS_OFF);
- }
- startAnimation = true;
+ if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
+ offMask |= KEYBOARD_BRIGHT_BIT;
} else {
- if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
- offMask |= KEYBOARD_BRIGHT_BIT;
- } else {
- onMask |= KEYBOARD_BRIGHT_BIT;
- }
+ onMask |= KEYBOARD_BRIGHT_BIT;
}
}
if ((difference & BUTTON_BRIGHT_BIT) != 0) {
- if (ANIMATE_BUTTON_LIGHTS) {
- if ((newState & BUTTON_BRIGHT_BIT) == 0) {
- mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_OFF,
- ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- Power.BRIGHTNESS_ON);
- } else {
- mButtonBrightness.setTargetLocked(Power.BRIGHTNESS_ON,
- ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- Power.BRIGHTNESS_OFF);
- }
- startAnimation = true;
+ if ((newState & BUTTON_BRIGHT_BIT) == 0) {
+ offMask |= BUTTON_BRIGHT_BIT;
} else {
- if ((newState & BUTTON_BRIGHT_BIT) == 0) {
- offMask |= BUTTON_BRIGHT_BIT;
- } else {
- onMask |= BUTTON_BRIGHT_BIT;
- }
+ onMask |= BUTTON_BRIGHT_BIT;
}
}
if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
- if (ANIMATE_SCREEN_LIGHTS) {
- int nominalCurrentValue = -1;
- // If there was an actual difference in the light state, then
- // figure out the "ideal" current value based on the previous
- // state. Otherwise, this is a change due to the brightness
- // override, so we want to animate from whatever the current
- // value is.
- if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
- switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) {
- case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT:
- nominalCurrentValue = preferredBrightness;
- break;
- case SCREEN_ON_BIT:
- nominalCurrentValue = Power.BRIGHTNESS_DIM;
- break;
- case 0:
- nominalCurrentValue = Power.BRIGHTNESS_OFF;
- break;
- case SCREEN_BRIGHT_BIT:
- default:
- // not possible
- nominalCurrentValue = (int)mScreenBrightness.curValue;
- break;
- }
+ int nominalCurrentValue = -1;
+ // If there was an actual difference in the light state, then
+ // figure out the "ideal" current value based on the previous
+ // state. Otherwise, this is a change due to the brightness
+ // override, so we want to animate from whatever the current
+ // value is.
+ if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
+ switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) {
+ case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT:
+ nominalCurrentValue = preferredBrightness;
+ break;
+ case SCREEN_ON_BIT:
+ nominalCurrentValue = Power.BRIGHTNESS_DIM;
+ break;
+ case 0:
+ nominalCurrentValue = Power.BRIGHTNESS_OFF;
+ break;
+ case SCREEN_BRIGHT_BIT:
+ default:
+ // not possible
+ nominalCurrentValue = (int)mScreenBrightness.curValue;
+ break;
}
- int brightness = preferredBrightness;
- int steps = ANIM_STEPS;
- if ((newState & SCREEN_BRIGHT_BIT) == 0) {
- // dim or turn off backlight, depending on if the screen is on
- // the scale is because the brightness ramp isn't linear and this biases
- // it so the later parts take longer.
- final float scale = 1.5f;
- float ratio = (((float)Power.BRIGHTNESS_DIM)/preferredBrightness);
- if (ratio > 1.0f) ratio = 1.0f;
- if ((newState & SCREEN_ON_BIT) == 0) {
- if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
- // was bright
- steps = ANIM_STEPS;
- } else {
- // was dim
- steps = (int)(ANIM_STEPS*ratio*scale);
- }
- brightness = Power.BRIGHTNESS_OFF;
+ }
+ int brightness = preferredBrightness;
+ int steps = ANIM_STEPS;
+ if ((newState & SCREEN_BRIGHT_BIT) == 0) {
+ // dim or turn off backlight, depending on if the screen is on
+ // the scale is because the brightness ramp isn't linear and this biases
+ // it so the later parts take longer.
+ final float scale = 1.5f;
+ float ratio = (((float)Power.BRIGHTNESS_DIM)/preferredBrightness);
+ if (ratio > 1.0f) ratio = 1.0f;
+ if ((newState & SCREEN_ON_BIT) == 0) {
+ if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
+ // was bright
+ steps = ANIM_STEPS;
} else {
- if ((oldState & SCREEN_ON_BIT) != 0) {
- // was bright
- steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
- } else {
- // was dim
- steps = (int)(ANIM_STEPS*ratio);
- }
- if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
- // If the "stay on while plugged in" option is
- // turned on, then the screen will often not
- // automatically turn off while plugged in. To
- // still have a sense of when it is inactive, we
- // will then count going dim as turning off.
- mScreenOffTime = SystemClock.elapsedRealtime();
- }
- brightness = Power.BRIGHTNESS_DIM;
+ // was dim
+ steps = (int)(ANIM_STEPS*ratio*scale);
}
- }
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- // Nothing interesting to do.
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- if (mScreenBrightness.setTargetLocked(brightness,
- steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue)) {
- startAnimation = true;
- }
- } else {
- if ((newState & SCREEN_BRIGHT_BIT) == 0) {
- // dim or turn off backlight, depending on if the screen is on
- if ((newState & SCREEN_ON_BIT) == 0) {
- offMask |= SCREEN_BRIGHT_BIT;
- } else {
- dimMask |= SCREEN_BRIGHT_BIT;
- }
+ brightness = Power.BRIGHTNESS_OFF;
} else {
- onMask |= SCREEN_BRIGHT_BIT;
+ if ((oldState & SCREEN_ON_BIT) != 0) {
+ // was bright
+ steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
+ } else {
+ // was dim
+ steps = (int)(ANIM_STEPS*ratio);
+ }
+ if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
+ // If the "stay on while plugged in" option is
+ // turned on, then the screen will often not
+ // automatically turn off while plugged in. To
+ // still have a sense of when it is inactive, we
+ // will then count going dim as turning off.
+ mScreenOffTime = SystemClock.elapsedRealtime();
+ }
+ brightness = Power.BRIGHTNESS_DIM;
}
}
- }
-
- if (startAnimation) {
- if (mSpew) {
- Slog.i(TAG, "Scheduling light animator!");
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenBrightness(brightness);
+ } catch (RemoteException e) {
+ // Nothing interesting to do.
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mHandler.removeCallbacks(mLightAnimator);
- mHandler.post(mLightAnimator);
+ mScreenBrightness.setTargetLocked(brightness, steps,
+ INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
}
if (offMask != 0) {
@@ -1934,7 +1911,7 @@
}
}
- class BrightnessState {
+ class BrightnessState implements Runnable {
final int mask;
boolean initialized;
@@ -1954,13 +1931,13 @@
+ " delta=" + delta);
}
- boolean setTargetLocked(int target, int stepsToTarget, int initialValue,
+ void setTargetLocked(int target, int stepsToTarget, int initialValue,
int nominalCurrentValue) {
if (!initialized) {
initialized = true;
curValue = (float)initialValue;
} else if (targetValue == target) {
- return false;
+ return;
}
targetValue = target;
delta = (targetValue -
@@ -1974,7 +1951,12 @@
+ noticeMe);
}
animating = true;
- return true;
+
+ if (mSpew) {
+ Slog.i(TAG, "scheduling light animator");
+ }
+ mScreenOffHandler.removeCallbacks(this);
+ mScreenOffHandler.post(this);
}
boolean stepLocked() {
@@ -2000,32 +1982,50 @@
more = false;
}
}
- //Slog.i(TAG, "Animating brightess " + curIntValue + ": " + mask);
+ if (mSpew) Slog.d(TAG, "Animating curIntValue=" + curIntValue + ": " + mask);
setLightBrightness(mask, curIntValue);
+ finishAnimation(more, curIntValue);
+ return more;
+ }
+
+ void jumpToTarget() {
+ if (mSpew) Slog.d(TAG, "jumpToTarget targetValue=" + targetValue + ": " + mask);
+ setLightBrightness(mask, targetValue);
+ final int tv = targetValue;
+ curValue = tv;
+ targetValue = -1;
+ finishAnimation(false, tv);
+ }
+
+ private void finishAnimation(boolean more, int curIntValue) {
animating = more;
if (!more) {
if (mask == SCREEN_BRIGHT_BIT && curIntValue == Power.BRIGHTNESS_OFF) {
screenOffFinishedAnimatingLocked(mScreenOffReason);
}
}
- return more;
}
- }
- private class LightAnimator implements Runnable {
public void run() {
- synchronized (mLocks) {
- long now = SystemClock.uptimeMillis();
- boolean more = mScreenBrightness.stepLocked();
- if (mKeyboardBrightness.stepLocked()) {
- more = true;
+ if (mAnimateScreenLights) {
+ synchronized (mLocks) {
+ long now = SystemClock.uptimeMillis();
+ boolean more = mScreenBrightness.stepLocked();
+ if (more) {
+ mScreenOffHandler.postAtTime(this, now+(1000/60));
+ }
}
- if (mButtonBrightness.stepLocked()) {
- more = true;
+ } else {
+ boolean animate;
+ boolean jump;
+ synchronized (mLocks) {
+ jump = animating; // we haven't already run this animation
+ animate = jump && targetValue == Power.BRIGHTNESS_OFF; // we're turning off
}
- if (more) {
- mHandler.postAtTime(mLightAnimator, now+(1000/60));
+ if (animate) {
+ nativeStartSurfaceFlingerAnimation();
}
+ mScreenBrightness.jumpToTarget();
}
}
}
@@ -2343,49 +2343,15 @@
Slog.d(TAG, "keyboardValue " + keyboardValue);
}
- boolean startAnimation = false;
if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
- if (ANIMATE_SCREEN_LIGHTS) {
- if (mScreenBrightness.setTargetLocked(lcdValue,
- AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_SCREEN_BRIGHTNESS,
- (int)mScreenBrightness.curValue)) {
- startAnimation = true;
- }
- } else {
- int brightnessMode = (mAutoBrightessEnabled
- ? LightsService.BRIGHTNESS_MODE_SENSOR
- : LightsService.BRIGHTNESS_MODE_USER);
- mLcdLight.setBrightness(lcdValue, brightnessMode);
- }
+ mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS,
+ INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue);
}
if (mButtonBrightnessOverride < 0) {
- if (ANIMATE_BUTTON_LIGHTS) {
- if (mButtonBrightness.setTargetLocked(buttonValue,
- AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- (int)mButtonBrightness.curValue)) {
- startAnimation = true;
- }
- } else {
- mButtonLight.setBrightness(buttonValue);
- }
+ mButtonLight.setBrightness(buttonValue);
}
if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) {
- if (ANIMATE_KEYBOARD_LIGHTS) {
- if (mKeyboardBrightness.setTargetLocked(keyboardValue,
- AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS,
- (int)mKeyboardBrightness.curValue)) {
- startAnimation = true;
- }
- } else {
- mKeyboardLight.setBrightness(keyboardValue);
- }
- }
- if (startAnimation) {
- if (mDebugLightSensor) {
- Slog.i(TAG, "lightSensorChangedLocked scheduling light animator");
- }
- mHandler.removeCallbacks(mLightAnimator);
- mHandler.post(mLightAnimator);
+ mKeyboardLight.setBrightness(keyboardValue);
}
}
}
@@ -2753,6 +2719,7 @@
}
}
+ // for watchdog
public void monitor() {
synchronized (mLocks) { }
}
@@ -2772,34 +2739,23 @@
public void setBacklightBrightness(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
// Don't let applications turn the screen all the way off
- brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
- mLcdLight.setBrightness(brightness);
- mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
- mButtonLight.setBrightness(brightness);
- long identity = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteScreenBrightness(brightness);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ synchronized (mLocks) {
+ brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
+ mLcdLight.setBrightness(brightness);
+ mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
+ mButtonLight.setBrightness(brightness);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteScreenBrightness(brightness);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
- // update our animation state
- if (ANIMATE_SCREEN_LIGHTS) {
- mScreenBrightness.curValue = brightness;
- mScreenBrightness.animating = false;
- mScreenBrightness.targetValue = -1;
- }
- if (ANIMATE_KEYBOARD_LIGHTS) {
- mKeyboardBrightness.curValue = brightness;
- mKeyboardBrightness.animating = false;
- mKeyboardBrightness.targetValue = -1;
- }
- if (ANIMATE_BUTTON_LIGHTS) {
- mButtonBrightness.curValue = brightness;
- mButtonBrightness.animating = false;
- mButtonBrightness.targetValue = -1;
+ // update our animation state
+ mScreenBrightness.targetValue = brightness;
+ mScreenBrightness.jumpToTarget();
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 016ddcd..86c7bdf 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2535,7 +2535,7 @@
mWaitingActivityLaunched.add(outResult);
do {
try {
- wait();
+ mService.wait();
} catch (InterruptedException e) {
}
} while (!outResult.timeout && outResult.who == null);
@@ -2551,7 +2551,7 @@
mWaitingActivityVisible.add(outResult);
do {
try {
- wait();
+ mService.wait();
} catch (InterruptedException e) {
}
} while (!outResult.timeout && outResult.who == null);
diff --git a/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java b/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java
index 431b798..bddbafc 100644
--- a/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java
+++ b/services/java/com/trustedlogic/trustednfc/android/server/NfcService.java
@@ -17,44 +17,43 @@
package com.trustedlogic.trustednfc.android.server;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
-import java.util.Set;
-import com.trustedlogic.trustednfc.android.ILlcpConnectionlessSocket;
-import com.trustedlogic.trustednfc.android.ILlcpServiceSocket;
-import com.trustedlogic.trustednfc.android.INfcManager;
-import com.trustedlogic.trustednfc.android.ILlcpSocket;
-import com.trustedlogic.trustednfc.android.INfcTag;
-import com.trustedlogic.trustednfc.android.IP2pInitiator;
-import com.trustedlogic.trustednfc.android.IP2pTarget;
-import com.trustedlogic.trustednfc.android.LlcpPacket;
-import com.trustedlogic.trustednfc.android.NdefMessage;
-import com.trustedlogic.trustednfc.android.NfcException;
-import com.trustedlogic.trustednfc.android.NfcManager;
+import android.nfc.ErrorCodes;
+import android.nfc.FormatException;
+import android.nfc.ILlcpConnectionlessSocket;
+import android.nfc.ILlcpServiceSocket;
+import android.nfc.INfcAdapter;
+import android.nfc.ILlcpSocket;
+import android.nfc.INfcTag;
+import android.nfc.IP2pInitiator;
+import android.nfc.IP2pTarget;
+import android.nfc.LlcpPacket;
+import android.nfc.NdefMessage;
+import android.nfc.Tag;
+//import android.nfc.NfcException;
+//import android.nfc.NfcManager;
+import android.nfc.NfcAdapter;
import com.trustedlogic.trustednfc.android.internal.NativeLlcpConnectionlessSocket;
import com.trustedlogic.trustednfc.android.internal.NativeLlcpServiceSocket;
import com.trustedlogic.trustednfc.android.internal.NativeLlcpSocket;
import com.trustedlogic.trustednfc.android.internal.NativeNfcManager;
import com.trustedlogic.trustednfc.android.internal.NativeNfcTag;
import com.trustedlogic.trustednfc.android.internal.NativeP2pDevice;
-import com.trustedlogic.trustednfc.android.internal.ErrorCodes;
-
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-public class NfcService extends INfcManager.Stub implements Runnable {
+public class NfcService extends INfcAdapter.Stub implements Runnable {
/**
* NFC Service tag
@@ -188,15 +187,15 @@
private static final String PROPERTY_NFC_DISCOVERY_NFCIP_VALUE = "discovery.nfcip";
- private Context mContext;
+ private final Context mContext;
- private HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
+ private final HashMap<Integer, Object> mObjectMap = new HashMap<Integer, Object>();
- private HashMap<Integer, Object> mSocketMap = new HashMap<Integer, Object>();
+ private final HashMap<Integer, Object> mSocketMap = new HashMap<Integer, Object>();
- private LinkedList<RegisteredSocket> mRegisteredSocketList = new LinkedList<RegisteredSocket>();
+ private final LinkedList<RegisteredSocket> mRegisteredSocketList = new LinkedList<RegisteredSocket>();
- private int mLlcpLinkState = NfcManager.LLCP_LINK_STATE_DEACTIVATED;
+ private int mLlcpLinkState = NfcAdapter.LLCP_LINK_STATE_DEACTIVATED;
private int mGeneratedSocketHandle = 0;
@@ -216,9 +215,42 @@
private boolean mOpenPending = false;
- private NativeNfcManager mManager;
+ private final NativeNfcManager mManager;
- private ILlcpSocket mLlcpSocket = new ILlcpSocket.Stub() {
+ private final ILlcpSocket mLlcpSocket = new ILlcpSocket.Stub() {
+
+ private final int CONNECT_FLAG = 0x01;
+ private final int CLOSE_FLAG = 0x02;
+ private final int RECV_FLAG = 0x04;
+ private final int SEND_FLAG = 0x08;
+
+ private int concurrencyFlags;
+ private Object sync;
+
+ private void enterCritical(int mask, int current) {
+ int result = -1;
+ try {
+ while (result != 0) {
+ synchronized(this) {
+ result = concurrencyFlags & mask;
+ }
+ sync.wait();
+ }
+ }
+ catch(InterruptedException e) {
+ }
+ // Set flag
+ concurrencyFlags |= current;
+ }
+
+ private void leaveCritical(int current) {
+ synchronized(this) {
+ // Clear flag
+ concurrencyFlags &= ~current;
+ }
+ // Release waiting threads
+ sync.notifyAll();
+ }
public int close(int nativeHandle) throws RemoteException {
NativeLlcpSocket socket = null;
@@ -232,7 +264,7 @@
/* find the socket in the hmap */
socket = (NativeLlcpSocket) findSocket(nativeHandle);
if (socket != null) {
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
isSuccess = socket.doClose();
if (isSuccess) {
/* Remove the socket closed from the hmap */
@@ -476,7 +508,7 @@
};
- private ILlcpServiceSocket mLlcpServerSocketService = new ILlcpServiceSocket.Stub() {
+ private final ILlcpServiceSocket mLlcpServerSocketService = new ILlcpServiceSocket.Stub() {
public int accept(int nativeHandle) throws RemoteException {
NativeLlcpServiceSocket socket = null;
@@ -522,7 +554,7 @@
/* find the socket in the hmap */
socket = (NativeLlcpServiceSocket) findSocket(nativeHandle);
if (socket != null) {
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
isSuccess = socket.doClose();
if (isSuccess) {
/* Remove the socket closed from the hmap */
@@ -571,7 +603,7 @@
}
};
- private ILlcpConnectionlessSocket mLlcpConnectionlessSocketService = new ILlcpConnectionlessSocket.Stub() {
+ private final ILlcpConnectionlessSocket mLlcpConnectionlessSocketService = new ILlcpConnectionlessSocket.Stub() {
public void close(int nativeHandle) throws RemoteException {
NativeLlcpConnectionlessSocket socket = null;
@@ -585,7 +617,7 @@
/* find the socket in the hmap */
socket = (NativeLlcpConnectionlessSocket) findSocket(nativeHandle);
if (socket != null) {
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
isSuccess = socket.doClose();
if (isSuccess) {
/* Remove the socket closed from the hmap */
@@ -669,7 +701,7 @@
}
};
- private INfcTag mNfcTagService = new INfcTag.Stub() {
+ private final INfcTag mNfcTagService = new INfcTag.Stub() {
public int close(int nativeHandle) throws RemoteException {
NativeNfcTag tag = null;
@@ -807,34 +839,55 @@
/* Create an NdefMessage */
try {
return new NdefMessage(buf);
- } catch (NfcException e) {
+ } catch (FormatException e) {
return null;
}
}
return null;
}
- public boolean write(int nativeHandle, NdefMessage msg) throws RemoteException {
+ public int write(int nativeHandle, NdefMessage msg) throws RemoteException {
NativeNfcTag tag;
- boolean isSuccess = false;
// Check if NFC is enabled
if (!mIsNfcEnabled) {
- return isSuccess;
+ return ErrorCodes.ERROR_NOT_INITIALIZED;
}
/* find the tag in the hmap */
tag = (NativeNfcTag) findObject(nativeHandle);
- if (tag != null) {
- isSuccess = tag.doWrite(msg.toByteArray());
+ if (tag == null) {
+ return ErrorCodes.ERROR_IO;
}
- return isSuccess;
+
+ if (tag.doWrite(msg.toByteArray())) {
+ return ErrorCodes.SUCCESS;
+ }
+ else {
+ return ErrorCodes.ERROR_IO;
+ }
}
+ public int getLastError(int nativeHandle) throws RemoteException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getModeHint(int nativeHandle) throws RemoteException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int makeReadOnly(int nativeHandle) throws RemoteException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
};
- private IP2pInitiator mP2pInitiatorService = new IP2pInitiator.Stub() {
+ private final IP2pInitiator mP2pInitiatorService = new IP2pInitiator.Stub() {
public byte[] getGeneralBytes(int nativeHandle) throws RemoteException {
NativeP2pDevice device;
@@ -911,7 +964,7 @@
}
};
- private IP2pTarget mP2pTargetService = new IP2pTarget.Stub() {
+ private final IP2pTarget mP2pTargetService = new IP2pTarget.Stub() {
public int connect(int nativeHandle) throws RemoteException {
NativeP2pDevice device;
@@ -1033,8 +1086,8 @@
NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_ACTION));
mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter(
- NfcManager.LLCP_LINK_STATE_CHANGED_ACTION));
-
+ NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED));
+
mContext.registerReceiver(mNfcServiceReceiver, new IntentFilter(
NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION));
@@ -1088,7 +1141,7 @@
/* Store the socket handle */
int sockeHandle = mGeneratedSocketHandle;
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
NativeLlcpConnectionlessSocket socket;
socket = mManager.doCreateLlcpConnectionlessSocket(sap);
@@ -1167,7 +1220,7 @@
if (mNbSocketCreated < LLCP_SOCKET_NB_MAX) {
int sockeHandle = mGeneratedSocketHandle;
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
NativeLlcpServiceSocket socket;
socket = mManager.doCreateLlcpServiceSocket(sap, sn, miu, rw, linearBufferLength);
@@ -1251,7 +1304,7 @@
int sockeHandle = mGeneratedSocketHandle;
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
NativeLlcpSocket socket;
socket = mManager.doCreateLlcpSocket(sap, miu, rw, linearBufferLength);
@@ -1333,7 +1386,7 @@
mManager.doDeselectSecureElement(mSelectedSeId);
mNfcSecureElementState = 0;
mSelectedSeId = 0;
-
+
/* Store that a secure element is deselected */
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.NFC_SECURE_ELEMENT_ON, 0);
@@ -1341,9 +1394,9 @@
/* Reset Secure Element ID */
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.NFC_SECURE_ELEMENT_ID, 0);
-
- return ErrorCodes.SUCCESS;
+
+ return ErrorCodes.SUCCESS;
}
public boolean disable() throws RemoteException {
@@ -1615,30 +1668,10 @@
}
- public int openTagConnection() throws RemoteException {
- NativeNfcTag tag;
- // Check if NFC is enabled
- if (!mIsNfcEnabled) {
- return ErrorCodes.ERROR_NOT_INITIALIZED;
- }
+ public void openTagConnection(Tag tag) throws RemoteException {
+ NativeNfcTag nativeTag = new NativeNfcTag(tag.getHandle(), "", tag.getId());
- mContext.enforceCallingPermission(android.Manifest.permission.NFC_RAW,
- "NFC_RAW permission required to open NFC Tag connection");
- if (!mOpenPending) {
- mOpenPending = true;
- tag = mManager.doOpenTagConnection(mTimeout);
- if (tag != null) {
- mObjectMap.put(tag.getHandle(), tag);
- return tag.getHandle();
- } else {
- mOpenPending = false;
- /* Restart polling loop for notification */
- mManager.enableDiscovery(DISCOVERY_MODE_READER);
- return ErrorCodes.ERROR_IO;
- }
- } else {
- return ErrorCodes.ERROR_BUSY;
- }
+ mObjectMap.put(nativeTag.getHandle(), nativeTag);
}
public int selectSecureElement(int seId) throws RemoteException {
@@ -1646,7 +1679,7 @@
if (!mIsNfcEnabled) {
return ErrorCodes.ERROR_NOT_INITIALIZED;
}
-
+
if (mSelectedSeId == seId) {
return ErrorCodes.ERROR_SE_ALREADY_SELECTED;
}
@@ -1668,7 +1701,7 @@
/* Store the ID of the Secure Element Selected */
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.NFC_SECURE_ELEMENT_ID, mSelectedSeId);
-
+
mNfcSecureElementState = 1;
return ErrorCodes.SUCCESS;
@@ -1847,6 +1880,16 @@
return ErrorCodes.SUCCESS;
}
+ public NdefMessage localGet() throws RemoteException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void localSet(NdefMessage message) throws RemoteException {
+ // TODO Auto-generated method stub
+
+ }
+
// Reset all internals
private void reset() {
@@ -1856,7 +1899,7 @@
mRegisteredSocketList.clear();
// Reset variables
- mLlcpLinkState = NfcManager.LLCP_LINK_STATE_DEACTIVATED;
+ mLlcpLinkState = NfcAdapter.LLCP_LINK_STATE_DEACTIVATED;
mNbSocketCreated = 0;
mIsNfcEnabled = false;
mSelectedSeId = 0;
@@ -1869,6 +1912,9 @@
Object device = null;
device = mObjectMap.get(key);
+ if (device == null) {
+ Log.w(TAG, "Handle not found !");
+ }
return device;
}
@@ -1947,11 +1993,11 @@
* LLCP link in not activated
*/
private class RegisteredSocket {
- private int mType;
+ private final int mType;
- private int mHandle;
+ private final int mHandle;
- private int mSap;
+ private final int mSap;
private int mMiu;
@@ -1988,17 +2034,17 @@
}
}
- private BroadcastReceiver mNfcServiceReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mNfcServiceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Internal NFC Intent received");
/* LLCP Link deactivation */
- if (intent.getAction().equals(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION)) {
- mLlcpLinkState = intent.getIntExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA,
- NfcManager.LLCP_LINK_STATE_DEACTIVATED);
+ if (intent.getAction().equals(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED)) {
+ mLlcpLinkState = intent.getIntExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED,
+ NfcAdapter.LLCP_LINK_STATE_DEACTIVATED);
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_DEACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_DEACTIVATED) {
/* restart polling loop */
mManager.enableDiscovery(DISCOVERY_MODE_READER);
}
@@ -2010,9 +2056,9 @@
mLlcpLinkState = intent.getIntExtra(
NativeNfcManager.INTERNAL_LLCP_LINK_STATE_CHANGED_EXTRA,
- NfcManager.LLCP_LINK_STATE_DEACTIVATED);
+ NfcAdapter.LLCP_LINK_STATE_DEACTIVATED);
- if (mLlcpLinkState == NfcManager.LLCP_LINK_STATE_ACTIVATED) {
+ if (mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED) {
/* check if sockets are registered */
ListIterator<RegisteredSocket> it = mRegisteredSocketList.listIterator();
@@ -2086,16 +2132,16 @@
/* Broadcast Intent Link LLCP activated */
Intent LlcpLinkIntent = new Intent();
- LlcpLinkIntent.setAction(NfcManager.LLCP_LINK_STATE_CHANGED_ACTION);
+ LlcpLinkIntent.setAction(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED);
- LlcpLinkIntent.putExtra(NfcManager.LLCP_LINK_STATE_CHANGED_EXTRA,
- NfcManager.LLCP_LINK_STATE_ACTIVATED);
+ LlcpLinkIntent.putExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED,
+ NfcAdapter.LLCP_LINK_STATE_ACTIVATED);
Log.d(TAG, "Broadcasting LLCP activation");
mContext.sendOrderedBroadcast(LlcpLinkIntent,
android.Manifest.permission.NFC_LLCP);
}
- }
+ }
/* Target Deactivated */
else if (intent.getAction().equals(
NativeNfcManager.INTERNAL_TARGET_DESELECTED_ACTION)) {
@@ -2104,8 +2150,8 @@
}
/* Restart polling loop for notification */
mManager.enableDiscovery(DISCOVERY_MODE_READER);
-
+
}
}
};
-}
+}
\ No newline at end of file
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index cdc0a6f..c90879d 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -23,7 +23,8 @@
libnativehelper \
libsystem_server \
libutils \
- libui
+ libui \
+ libsurfaceflinger_client
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_OS),linux)
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index a0b0aba..1bd1874 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -842,31 +842,35 @@
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
- const int32_t WM_ACTION_PASS_TO_USER = 1;
- const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
- const int32_t WM_ACTION_GO_TO_SLEEP = 4;
+ // Policy:
+ // - Ignore untrusted events and pass them along.
+ // - Ask the window manager what to do with normal events and trusted injected events.
+ // - For normal events wake and brighten the screen if currently off or dim.
+ if ((policyFlags & POLICY_FLAG_TRUSTED)) {
+ const int32_t WM_ACTION_PASS_TO_USER = 1;
+ const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
+ const int32_t WM_ACTION_GO_TO_SLEEP = 4;
- bool isScreenOn = this->isScreenOn();
- bool isScreenBright = this->isScreenBright();
+ bool isScreenOn = this->isScreenOn();
+ bool isScreenBright = this->isScreenBright();
- JNIEnv* env = jniEnv();
- jint wmActions = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.interceptKeyBeforeQueueing,
- when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn);
- if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
- wmActions = 0;
- }
-
- if (policyFlags & POLICY_FLAG_TRUSTED) {
- if (! isScreenOn) {
- // Key presses and releases wake the device.
- policyFlags |= POLICY_FLAG_WOKE_HERE;
- flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+ JNIEnv* env = jniEnv();
+ jint wmActions = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeQueueing,
+ when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn);
+ if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
+ wmActions = 0;
}
- if (! isScreenBright) {
- // Key presses and releases brighten the screen if dimmed.
- policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ if (!(flags & POLICY_FLAG_INJECTED)) {
+ if (!isScreenOn) {
+ policyFlags |= POLICY_FLAG_WOKE_HERE;
+ flags |= AKEY_EVENT_FLAG_WOKE_HERE;
+ }
+
+ if (!isScreenBright) {
+ policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ }
}
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
@@ -876,9 +880,11 @@
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
}
- }
- if (wmActions & WM_ACTION_PASS_TO_USER) {
+ if (wmActions & WM_ACTION_PASS_TO_USER) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ }
+ } else {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
@@ -888,33 +894,47 @@
LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags);
#endif
- if (isScreenOn()) {
- // Only dispatch events when the device is awake.
- // Do not wake the device.
- policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ // Policy:
+ // - Ignore untrusted events and pass them along.
+ // - No special filtering for injected events required at this time.
+ // - Filter normal events based on screen state.
+ // - For normal events brighten (but do not wake) the screen if currently dim.
+ if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
+ if (isScreenOn()) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
- if ((policyFlags & POLICY_FLAG_TRUSTED) && !isScreenBright()) {
- // Brighten the screen if dimmed.
- policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ if (!isScreenBright()) {
+ policyFlags |= POLICY_FLAG_BRIGHT_HERE;
+ }
}
+ } else {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) {
- JNIEnv* env = jniEnv();
+ // Policy:
+ // - Ignore untrusted events and pass them along.
+ // - Filter normal events and trusted injected events through the window manager policy to
+ // handle the HOME key and the like.
+ if (policyFlags & POLICY_FLAG_TRUSTED) {
+ JNIEnv* env = jniEnv();
- // Note: inputChannel may be null.
- jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
- jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.interceptKeyBeforeDispatching,
- inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
- keyEvent->getKeyCode(), keyEvent->getMetaState(),
- keyEvent->getRepeatCount(), policyFlags);
- bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
+ // Note: inputChannel may be null.
+ jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
+ jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeDispatching,
+ inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
- env->DeleteLocalRef(inputChannelObj);
- return consumed && ! error;
+ env->DeleteLocalRef(inputChannelObj);
+ return consumed && ! error;
+ } else {
+ return false;
+ }
}
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index 146c177..2ec20bd 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -20,9 +20,14 @@
#include "JNIHelp.h"
#include "jni.h"
+
#include <limits.h>
+
#include <android_runtime/AndroidRuntime.h>
#include <utils/Timers.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
#include "com_android_server_PowerManagerService.h"
namespace android {
@@ -119,6 +124,12 @@
gScreenBright = screenBright;
}
+static void android_server_PowerManagerService_nativeStartSurfaceFlingerAnimation(JNIEnv* env,
+ jobject obj) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ s->turnElectronBeamOff(0);
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gPowerManagerServiceMethods[] = {
@@ -127,6 +138,8 @@
(void*) android_server_PowerManagerService_nativeInit },
{ "nativeSetPowerState", "(ZZ)V",
(void*) android_server_PowerManagerService_nativeSetPowerState },
+ { "nativeStartSurfaceFlingerAnimation", "()V",
+ (void*) android_server_PowerManagerService_nativeStartSurfaceFlingerAnimation },
};
#define FIND_CLASS(var, className) \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index 1d09f84..fe9a5ab 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -359,7 +359,7 @@
DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
uint32_t displayIndex)
- : mCanDraw(true)
+ : mCanDraw(true), mScreenAcquired(true)
{
mDisplayEventThread = new DisplayEventThread(flinger);
if (mDisplayEventThread->initCheck() != NO_ERROR) {
@@ -374,18 +374,21 @@
mDisplayEventThread->requestExitAndWait();
}
+void DisplayHardwareBase::setCanDraw(bool canDraw)
+{
+ mCanDraw = canDraw;
+}
bool DisplayHardwareBase::canDraw() const
{
- return mCanDraw;
+ return mCanDraw && mScreenAcquired;
}
void DisplayHardwareBase::releaseScreen() const
{
status_t err = mDisplayEventThread->releaseScreen();
if (err >= 0) {
- //LOGD("screen given-up");
- mCanDraw = false;
+ mScreenAcquired = false;
}
}
@@ -393,9 +396,14 @@
{
status_t err = mDisplayEventThread->acquireScreen();
if (err >= 0) {
- //LOGD("screen returned");
mCanDraw = true;
+ mScreenAcquired = true;
}
}
+bool DisplayHardwareBase::isScreenAcquired() const
+{
+ return mScreenAcquired;
+}
+
}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index 8369bb8..fa6a0c4 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -40,7 +40,11 @@
// console managment
void releaseScreen() const;
void acquireScreen() const;
+ bool isScreenAcquired() const;
+
bool canDraw() const;
+ void setCanDraw(bool canDraw);
+
private:
class DisplayEventThreadBase : public Thread {
@@ -89,6 +93,7 @@
sp<DisplayEventThreadBase> mDisplayEventThread;
mutable int mCanDraw;
+ mutable int mScreenAcquired;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e5e87c6..a919ddf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -423,14 +423,14 @@
hw.acquireScreen();
}
- if (mDeferReleaseConsole && hw.canDraw()) {
+ if (mDeferReleaseConsole && hw.isScreenAcquired()) {
// We got the release signal before the acquire signal
mDeferReleaseConsole = false;
hw.releaseScreen();
}
if (what & eConsoleReleased) {
- if (hw.canDraw()) {
+ if (hw.isScreenAcquired()) {
hw.releaseScreen();
} else {
mDeferReleaseConsole = true;
@@ -1456,6 +1456,7 @@
case FREEZE_DISPLAY:
case UNFREEZE_DISPLAY:
case BOOT_FINISHED:
+ case TURN_ELECTRON_BEAM_OFF:
{
// codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
@@ -1544,6 +1545,231 @@
return err;
}
+
+// ---------------------------------------------------------------------------
+
+status_t SurfaceFlinger::turnElectronBeamOffImplLocked()
+{
+ status_t result = PERMISSION_DENIED;
+
+ if (!GLExtensions::getInstance().haveFramebufferObject())
+ return INVALID_OPERATION;
+
+ // get screen geometry
+ const int dpy = 0;
+ const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
+ if (!hw.canDraw()) {
+ // we're already off
+ return NO_ERROR;
+ }
+
+ const uint32_t hw_w = hw.getWidth();
+ const uint32_t hw_h = hw.getHeight();
+ const Region screenBounds(hw.bounds());
+ GLfloat u = 1;
+ GLfloat v = 1;
+
+ // make sure to clear all GL error flags
+ while ( glGetError() != GL_NO_ERROR ) ;
+
+ // create a FBO
+ GLuint name, tname;
+ glGenTextures(1, &tname);
+ glBindTexture(GL_TEXTURE_2D, tname);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, hw_w, hw_h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ if (glGetError() != GL_NO_ERROR) {
+ GLint tw = (2 << (31 - clz(hw_w)));
+ GLint th = (2 << (31 - clz(hw_h)));
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
+ u = GLfloat(hw_w) / tw;
+ v = GLfloat(hw_h) / th;
+ }
+ glGenFramebuffersOES(1, &name);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+ glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
+
+ GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+ if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+ // redraw the screen entirely...
+ glClearColor(0,0,0,1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+ const size_t count = layers.size();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<LayerBase>& layer(layers[i]);
+ layer->drawForSreenShot();
+ }
+ // back to main framebuffer
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+ glDisable(GL_SCISSOR_TEST);
+
+ GLfloat vtx[8];
+ const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} };
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, tname);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vtx);
+
+ class s_curve_interpolator {
+ const float nbFrames, s, v;
+ public:
+ s_curve_interpolator(int nbFrames, float s)
+ : nbFrames(1.0f / (nbFrames-1)), s(s),
+ v(1.0f + expf(-s + 0.5f*s)) {
+ }
+ float operator()(int f) {
+ const float x = f * nbFrames;
+ return ((1.0f/(1.0f + expf(-x*s + 0.5f*s))) - 0.5f) * v + 0.5f;
+ }
+ };
+
+ class v_stretch {
+ const GLfloat hw_w, hw_h;
+ public:
+ v_stretch(uint32_t hw_w, uint32_t hw_h)
+ : hw_w(hw_w), hw_h(hw_h) {
+ }
+ void operator()(GLfloat* vtx, float v) {
+ const GLfloat w = hw_w + (hw_w * v);
+ const GLfloat h = hw_h - (hw_h * v);
+ const GLfloat x = (hw_w - w) * 0.5f;
+ const GLfloat y = (hw_h - h) * 0.5f;
+ vtx[0] = x; vtx[1] = y;
+ vtx[2] = x; vtx[3] = y + h;
+ vtx[4] = x + w; vtx[5] = y + h;
+ vtx[6] = x + w; vtx[7] = y;
+ }
+ };
+
+ class h_stretch {
+ const GLfloat hw_w, hw_h;
+ public:
+ h_stretch(uint32_t hw_w, uint32_t hw_h)
+ : hw_w(hw_w), hw_h(hw_h) {
+ }
+ void operator()(GLfloat* vtx, float v) {
+ const GLfloat w = hw_w - (hw_w * v);
+ const GLfloat h = 1.0f;
+ const GLfloat x = (hw_w - w) * 0.5f;
+ const GLfloat y = (hw_h - h) * 0.5f;
+ vtx[0] = x; vtx[1] = y;
+ vtx[2] = x; vtx[3] = y + h;
+ vtx[4] = x + w; vtx[5] = y + h;
+ vtx[6] = x + w; vtx[7] = y;
+ }
+ };
+
+ // the full animation is 24 frames
+ const int nbFrames = 12;
+
+ v_stretch vverts(hw_w, hw_h);
+ s_curve_interpolator itr(nbFrames, 7.5f);
+ s_curve_interpolator itg(nbFrames, 8.0f);
+ s_curve_interpolator itb(nbFrames, 8.5f);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
+ for (int i=0 ; i<nbFrames ; i++) {
+ float x, y, w, h;
+ const float vr = itr(i);
+ const float vg = itg(i);
+ const float vb = itb(i);
+
+ // clear screen
+ glColorMask(1,1,1,1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glEnable(GL_TEXTURE_2D);
+
+ // draw the red plane
+ vverts(vtx, vr);
+ glColorMask(1,0,0,1);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the green plane
+ vverts(vtx, vg);
+ glColorMask(0,1,0,1);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the blue plane
+ vverts(vtx, vb);
+ glColorMask(0,0,1,1);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the white highlight (we use the last vertices)
+ glDisable(GL_TEXTURE_2D);
+ glColorMask(1,1,1,1);
+ glColor4f(vg, vg, vg, 1);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ hw.flip(screenBounds);
+ }
+
+ h_stretch hverts(hw_w, hw_h);
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+ glColorMask(1,1,1,1);
+ for (int i=0 ; i<nbFrames ; i++) {
+ const float v = itg(i);
+ hverts(vtx, v);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColor4f(1-v, 1-v, 1-v, 1);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ hw.flip(screenBounds);
+ }
+
+ glColorMask(1,1,1,1);
+ glEnable(GL_SCISSOR_TEST);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ result = NO_ERROR;
+ } else {
+ // release FBO resources
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+ result = BAD_VALUE;
+ }
+
+ glDeleteFramebuffersOES(1, &name);
+ glDeleteTextures(1, &tname);
+
+ if (result == NO_ERROR) {
+ DisplayHardware& hw(graphicPlane(dpy).editDisplayHardware());
+ hw.setCanDraw(false);
+ }
+
+ return result;
+}
+
+status_t SurfaceFlinger::turnElectronBeamOff(int32_t mode)
+{
+ if (!GLExtensions::getInstance().haveFramebufferObject())
+ return INVALID_OPERATION;
+
+ class MessageTurnElectronBeamOff : public MessageBase {
+ SurfaceFlinger* flinger;
+ status_t result;
+ public:
+ MessageTurnElectronBeamOff(SurfaceFlinger* flinger)
+ : flinger(flinger), result(PERMISSION_DENIED) {
+ }
+ status_t getResult() const {
+ return result;
+ }
+ virtual bool handler() {
+ Mutex::Autolock _l(flinger->mStateLock);
+ result = flinger->turnElectronBeamOffImplLocked();
+ return true;
+ }
+ };
+
+ sp<MessageBase> msg = new MessageTurnElectronBeamOff(this);
+ status_t res = postMessageSync(msg);
+ if (res == NO_ERROR) {
+ res = static_cast<MessageTurnElectronBeamOff*>( msg.get() )->getResult();
+ }
+ return res;
+}
+
// ---------------------------------------------------------------------------
status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
@@ -2005,6 +2231,10 @@
return *mHw;
}
+DisplayHardware& GraphicPlane::editDisplayHardware() {
+ return *mHw;
+}
+
const Transform& GraphicPlane::transform() const {
return mGlobalTransform;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f0a167b..f85a22b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -141,6 +141,7 @@
int getHeight() const;
const DisplayHardware& displayHardware() const;
+ DisplayHardware& editDisplayHardware();
const Transform& transform() const;
EGLDisplay getEGLDisplay() const;
@@ -200,6 +201,7 @@
PixelFormat* format,
uint32_t reqWidth,
uint32_t reqHeight);
+ virtual status_t turnElectronBeamOff(int32_t mode);
void screenReleased(DisplayID dpy);
void screenAcquired(DisplayID dpy);
@@ -325,6 +327,8 @@
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t reqWidth = 0, uint32_t reqHeight = 0);
+ status_t turnElectronBeamOffImplLocked();
+
friend class FreezeLock;
sp<FreezeLock> getFreezeLock() const;
inline void incFreezeCount() {
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index b09df82..3f0ec0a 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -56,7 +56,7 @@
private static final String LOG_TAG ="CallManager";
private static final boolean DBG = true;
- private static final boolean VDBG = true;
+ private static final boolean VDBG = false;
private static final int EVENT_DISCONNECT = 100;
private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
@@ -292,7 +292,7 @@
if (basePhone != null && !mPhones.contains(basePhone)) {
- if (VDBG) {
+ if (DBG) {
Log.d(LOG_TAG, "registerPhone(" +
phone.getPhoneName() + " " + phone + ")");
}
@@ -319,7 +319,7 @@
if (basePhone != null && mPhones.contains(basePhone)) {
- if (VDBG) {
+ if (DBG) {
Log.d(LOG_TAG, "unregisterPhone(" +
phone.getPhoneName() + " " + phone + ")");
}
@@ -487,7 +487,7 @@
boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
boolean sameChannel = (activePhone == ringingPhone);
- if (DBG) {
+ if (VDBG) {
Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel);
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 360d35e..1e9b930 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -383,8 +383,8 @@
*/
public String toString() {
return new StringBuilder(384)
- .append("\nname: " + name)
- .append("\nphoneNumber: " + phoneNumber)
+ .append("\nname: " + /*name*/ "nnnnnn")
+ .append("\nphoneNumber: " + /*phoneNumber*/ "xxxxxxx")
.append("\ncnapName: " + cnapName)
.append("\nnumberPresentation: " + numberPresentation)
.append("\nnamePresentation: " + namePresentation)
@@ -395,8 +395,8 @@
.append("\nphotoResource: " + photoResource)
.append("\nperson_id: " + person_id)
.append("\nneedUpdate: " + needUpdate)
- .append("\ncontactRefUri: " + contactRefUri)
- .append("\ncontactRingtoneUri: " + contactRefUri)
+ .append("\ncontactRefUri: " + /*contactRefUri*/ "xxxxxxx")
+ .append("\ncontactRingtoneUri: " + /*contactRefUri*/ "xxxxxxx")
.append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
.append("\ncachedPhoto: " + cachedPhoto)
.append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 25ca559..3419567 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -135,7 +135,7 @@
} else {
if (DBG) log("Processing event: " + cw.event + " token (arg1): " + msg.arg1 +
- " command: " + msg.what + " query URI: " + args.uri);
+ " command: " + msg.what + " query URI: " + sanitizeUriToString(args.uri));
switch (cw.event) {
case EVENT_NEW_QUERY:
@@ -297,7 +297,7 @@
OnQueryCompleteListener listener, Object cookie) {
if (DBG) {
log("##### CallerInfoAsyncQuery startQuery()... #####");
- log("- number: " + number);
+ log("- number: " + /*number*/ "xxxxxxx");
log("- cookie: " + cookie);
}
@@ -309,7 +309,7 @@
if (PhoneNumberUtils.isUriNumber(number)) {
// "number" is really a SIP address.
- if (DBG) log(" - Treating number as a SIP address: " + number);
+ if (DBG) log(" - Treating number as a SIP address: " + /*number*/ "xxxxxxx");
// We look up SIP addresses directly in the Data table:
contactRef = Data.CONTENT_URI;
@@ -341,7 +341,7 @@
}
if (DBG) {
- log("==> contactRef: " + contactRef);
+ log("==> contactRef: " + sanitizeUriToString(contactRef));
log("==> selection: " + selection);
if (selectionArgs != null) {
for (int i = 0; i < selectionArgs.length; i++) {
@@ -383,8 +383,8 @@
*/
public void addQueryListener(int token, OnQueryCompleteListener listener, Object cookie) {
- if (DBG) log("adding listener to query: " + mHandler.mQueryUri + " handler: " +
- mHandler.toString());
+ if (DBG) log("adding listener to query: " + sanitizeUriToString(mHandler.mQueryUri) +
+ " handler: " + mHandler.toString());
//create cookieWrapper, add query request to end of queue.
CookieWrapper cw = new CookieWrapper();
@@ -418,6 +418,20 @@
mHandler = null;
}
+ private static String sanitizeUriToString(Uri uri) {
+ if (uri != null) {
+ String uriString = uri.toString();
+ int indexOfLastSlash = uriString.lastIndexOf('/');
+ if (indexOfLastSlash > 0) {
+ return uriString.substring(0, indexOfLastSlash) + "/xxxxxxx";
+ } else {
+ return uriString;
+ }
+ } else {
+ return "";
+ }
+ }
+
/**
* static logging method
*/
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index d711a80..b14896a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -238,7 +238,7 @@
msisdn = number;
msisdnTag = alphaTag;
- if(DBG) log("Set MSISDN: " + msisdnTag +" " + msisdn);
+ if(DBG) log("Set MSISDN: " + msisdnTag + " " + /*msisdn*/ "xxxxxxx");
AdnRecord adn = new AdnRecord(msisdnTag, msisdn);
@@ -496,7 +496,7 @@
imsi = null;
}
- Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxxxx");
+ Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxx");
if (mncLength == UNKNOWN) {
// the SIM has told us all it knows, but it didn't know the mnc length.
@@ -619,7 +619,7 @@
msisdn = adn.getNumber();
msisdnTag = adn.getAlphaTag();
- Log.d(LOG_TAG, "MSISDN: " + msisdn);
+ Log.d(LOG_TAG, "MSISDN: " + /*msisdn*/ "xxxxxxx");
break;
case EVENT_SET_MSISDN_DONE:
diff --git a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
index dc4b27b..d546a08 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -56,8 +56,8 @@
private DisconnectCause mCause = DisconnectCause.NOT_DISCONNECTED;
private PostDialState postDialState = PostDialState.NOT_STARTED;
- SipConnectionBase(String calleeSipUri) {
- dialString = calleeSipUri;
+ SipConnectionBase(String dialString) {
+ this.dialString = dialString;
postDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 67f13bd..1968552 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -387,7 +387,8 @@
try {
SipProfile callee =
new SipProfile.Builder(calleeSipUri).build();
- SipConnection c = new SipConnection(this, callee);
+ SipConnection c = new SipConnection(this, callee,
+ originalNumber);
connections.add(c);
c.dial();
setState(Call.State.DIALING);
@@ -578,6 +579,7 @@
private SipAudioCall mSipAudioCall;
private Call.State mState = Call.State.IDLE;
private SipProfile mPeer;
+ private String mOriginalNumber; // may be a PSTN number
private boolean mIncoming = false;
private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() {
@@ -659,10 +661,16 @@
}
};
- public SipConnection(SipCall owner, SipProfile callee) {
- super(getUriString(callee));
+ public SipConnection(SipCall owner, SipProfile callee,
+ String originalNumber) {
+ super(originalNumber);
mOwner = owner;
mPeer = callee;
+ mOriginalNumber = originalNumber;
+ }
+
+ public SipConnection(SipCall owner, SipProfile callee) {
+ this(owner, callee, getUriString(callee));
}
void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) {
@@ -735,7 +743,10 @@
@Override
public String getAddress() {
- return getUriString(mPeer);
+ // Phone app uses this to query caller ID. Return the original dial
+ // number (which may be a PSTN number) instead of the peer's SIP
+ // URI.
+ return mOriginalNumber;
}
@Override
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index ee554b5..42b4e7c 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -126,9 +126,9 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mScreenOn = true;
- } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOn = false;
+ } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ mScreenOn = true;
}
}
};
@@ -868,6 +868,7 @@
case SipErrorCode.SERVER_UNREACHABLE:
if (DEBUG) Log.d(TAG, " pause auto-registration");
stop();
+ break;
default:
restartLater();
}
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 57b3710..b5f8d39 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -153,7 +153,13 @@
}
synchronized void onConnectivityChanged() {
- for (SipSessionImpl s : mSessionMap.values()) {
+ SipSessionImpl[] ss = mSessionMap.values().toArray(
+ new SipSessionImpl[mSessionMap.size()]);
+ // Iterate on the copied array instead of directly on mSessionMap to
+ // avoid ConcurrentModificationException being thrown when
+ // SipSessionImpl removes itself from mSessionMap in onError() in the
+ // following loop.
+ for (SipSessionImpl s : ss) {
s.onError(SipErrorCode.DATA_CONNECTION_LOST,
"data connection lost");
}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 281077c..06f6696 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1285,15 +1285,13 @@
if (macaddr != null) {
mWifiInfo.setMacAddress(macaddr);
}
- if (mRunState == RUN_STATE_STARTING) {
- mRunState = RUN_STATE_RUNNING;
- if (!mIsScanOnly) {
- reconnectCommand();
- } else {
- // In some situations, supplicant needs to be kickstarted to
- // start the background scanning
- scan(true);
- }
+ mRunState = RUN_STATE_RUNNING;
+ if (!mIsScanOnly) {
+ reconnectCommand();
+ } else {
+ // In some situations, supplicant needs to be kickstarted to
+ // start the background scanning
+ scan(true);
}
}
break;
@@ -1613,12 +1611,10 @@
}
public synchronized boolean restart() {
- if (mRunState == RUN_STATE_STOPPED) {
+ if (isDriverStopped()) {
mRunState = RUN_STATE_STARTING;
resetConnections(true);
return startDriver();
- } else if (mRunState == RUN_STATE_STOPPING) {
- mRunState = RUN_STATE_STARTING;
}
return true;
}