diff options
Diffstat (limited to 'core')
44 files changed, 1948 insertions, 1156 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 055984f..804aeaa 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -655,7 +655,7 @@ public class Activity extends ContextThemeWrapper boolean mCalled; boolean mCheckedForLoaderManager; boolean mLoadersStarted; - private boolean mResumed; + /*package*/ boolean mResumed; private boolean mStopped; boolean mFinished; boolean mStartedActivity; @@ -4363,9 +4363,8 @@ public class Activity extends ContextThemeWrapper mLastNonConfigurationInstances = null; - // First call onResume() -before- setting mResumed, so we don't - // send out any status bar / menu notifications the client makes. mCalled = false; + // mResumed is set by the instrumentation mInstrumentation.callActivityOnResume(this); if (!mCalled) { throw new SuperNotCalledException( @@ -4374,7 +4373,6 @@ public class Activity extends ContextThemeWrapper } // Now really resume, and install the current status bar and menu. - mResumed = true; mCalled = false; mFragments.dispatchResume(); @@ -4399,6 +4397,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } + mResumed = false; } final void performUserLeaving() { @@ -4461,7 +4460,10 @@ public class Activity extends ContextThemeWrapper } } - final boolean isResumed() { + /** + * @hide + */ + public final boolean isResumed() { return mResumed; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 960b943..8f9a76b 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -193,6 +193,9 @@ public final class ActivityThread { final HashMap<IBinder, ProviderClientRecord> mLocalProviders = new HashMap<IBinder, ProviderClientRecord>(); + final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners + = new HashMap<Activity, ArrayList<OnActivityPausedListener>>(); + final GcIdler mGcIdler = new GcIdler(); boolean mGcIdlerScheduled = false; @@ -1514,6 +1517,28 @@ public final class ActivityThread { } } + public void registerOnActivityPausedListener(Activity activity, + OnActivityPausedListener listener) { + synchronized (mOnPauseListeners) { + ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity); + if (list == null) { + list = new ArrayList<OnActivityPausedListener>(); + mOnPauseListeners.put(activity, list); + } + list.add(listener); + } + } + + public void unregisterOnActivityPausedListener(Activity activity, + OnActivityPausedListener listener) { + synchronized (mOnPauseListeners) { + ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity); + if (list != null) { + list.remove(listener); + } + } + } + public final ActivityInfo resolveActivityInfo(Intent intent) { ActivityInfo aInfo = intent.resolveActivityInfo( mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES); @@ -2454,6 +2479,17 @@ public final class ActivityThread { } } r.paused = true; + + // Notify any outstanding on paused listeners + ArrayList<OnActivityPausedListener> listeners; + synchronized (mOnPauseListeners) { + listeners = mOnPauseListeners.remove(r.activity); + } + int size = (listeners != null ? listeners.size() : 0); + for (int i = 0; i < size; i++) { + listeners.get(i).onPaused(r.activity); + } + return state; } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index ad811d8..cd278be 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1149,6 +1149,7 @@ public class Instrumentation { * @param activity The activity being resumed. */ public void callActivityOnResume(Activity activity) { + activity.mResumed = true; activity.onResume(); if (mActivityMonitors != null) { diff --git a/core/java/android/app/OnActivityPausedListener.java b/core/java/android/app/OnActivityPausedListener.java new file mode 100644 index 0000000..379f133 --- /dev/null +++ b/core/java/android/app/OnActivityPausedListener.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.app; + +/** + * A listener that is called when an Activity is paused. Since this is tracked client side + * it should not be trusted to represent the exact current state, but can be used as a hint + * for cleanup. + * + * @hide + */ +public interface OnActivityPausedListener { + /** + * Called when the given activity is paused. + */ + public void onPaused(Activity activity); +} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index da518c2..2d03e7c 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1579,9 +1579,11 @@ public abstract class ContentResolver { @Override protected void finalize() throws Throwable { + // TODO: integrate CloseGuard support. try { if(!mCloseFlag) { - ContentResolver.this.releaseProvider(mContentProvider); + Log.w(TAG, "Cursor finalized without prior close()"); + close(); } } finally { super.finalize(); diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 4a75514..f079e42 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -25,6 +25,7 @@ import android.os.ServiceManager; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.view.IRotationWatcher; import android.view.IWindowManager; import android.view.Surface; @@ -489,6 +490,8 @@ public class SensorManager private final Handler mHandler; private SensorEvent mValuesPool; public SparseBooleanArray mSensors = new SparseBooleanArray(); + public SparseBooleanArray mFirstEvent = new SparseBooleanArray(); + public SparseIntArray mSensorAccuracies = new SparseIntArray(); ListenerDelegate(SensorEventListener listener, Sensor sensor, Handler handler) { mSensorEventListener = listener; @@ -499,10 +502,30 @@ public class SensorManager mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { - SensorEvent t = (SensorEvent)msg.obj; - if (t.accuracy >= 0) { - mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); + final SensorEvent t = (SensorEvent)msg.obj; + final int handle = t.sensor.getHandle(); + + switch (t.sensor.getType()) { + // Only report accuracy for sensors that support it. + case Sensor.TYPE_MAGNETIC_FIELD: + case Sensor.TYPE_ORIENTATION: + // call onAccuracyChanged() only if the value changes + final int accuracy = mSensorAccuracies.get(handle); + if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { + mSensorAccuracies.put(handle, t.accuracy); + mSensorEventListener.onAccuracyChanged(t.sensor, t.accuracy); + } + break; + default: + // For other sensors, just report the accuracy once + if (mFirstEvent.get(handle) == false) { + mFirstEvent.put(handle, true); + mSensorEventListener.onAccuracyChanged( + t.sensor, SENSOR_STATUS_ACCURACY_HIGH); + } + break; } + mSensorEventListener.onSensorChanged(t); returnToPool(t); } diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index a663fb8..d439a48 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -16,8 +16,12 @@ package android.nfc; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.IntentFilter; import android.nfc.NdefMessage; import android.nfc.Tag; +import android.nfc.TechListParcel; import android.nfc.ILlcpSocket; import android.nfc.ILlcpServiceSocket; import android.nfc.ILlcpConnectionlessSocket; @@ -44,6 +48,11 @@ interface INfcAdapter NdefMessage localGet(); void localSet(in NdefMessage message); void openTagConnection(in Tag tag); + void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent, + in IntentFilter[] filters, in TechListParcel techLists); + void disableForegroundDispatch(in ComponentName activity); + void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg); + void disableForegroundNdefPush(in ComponentName activity); // Non-public methods // TODO: check and complete diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl index 5d222d9..57dc38c 100644 --- a/core/java/android/nfc/INfcTag.aidl +++ b/core/java/android/nfc/INfcTag.aidl @@ -17,6 +17,7 @@ package android.nfc; import android.nfc.NdefMessage; +import android.nfc.TransceiveResult; /** * @hide @@ -30,7 +31,7 @@ interface INfcTag byte[] getUid(int nativeHandle); boolean isNdef(int nativeHandle); boolean isPresent(int nativeHandle); - byte[] transceive(int nativeHandle, in byte[] data, boolean raw); + TransceiveResult transceive(int nativeHandle, in byte[] data, boolean raw); int getLastError(int nativeHandle); @@ -39,4 +40,7 @@ interface INfcTag int ndefMakeReadOnly(int nativeHandle); boolean ndefIsWritable(int nativeHandle); int formatNdef(int nativeHandle, in byte[] key); + + void setIsoDepTimeout(int timeout); + void resetIsoDepTimeout(); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 758c8a0..4808032 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -18,11 +18,16 @@ package android.nfc; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.app.ActivityThread; +import android.app.OnActivityPausedListener; +import android.app.PendingIntent; import android.content.Context; +import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.IBinder; +import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; @@ -43,7 +48,6 @@ public final class NfcAdapter { * * If any activities respond to this intent neither * {@link #ACTION_TECHNOLOGY_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; @@ -51,13 +55,12 @@ public final class NfcAdapter { /** * Intent to started when a tag is discovered. The data URI is formated as * {@code vnd.android.nfc://tag/} with the path having a directory entry for each technology - * in the {@link Tag#getTechnologyList()} is ascending order. + * in the {@link Tag#getTechList()} is sorted ascending order. * * This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before * {@link #ACTION_TAG_DISCOVERED} * * If any activities respond to this intent {@link #ACTION_TAG_DISCOVERED} will not be started. - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_TECHNOLOGY_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; @@ -76,7 +79,6 @@ public final class NfcAdapter { /** * Mandatory Tag extra for the ACTION_TAG intents. - * @hide */ public static final String EXTRA_TAG = "android.nfc.extra.TAG"; @@ -102,6 +104,20 @@ public final class NfcAdapter { "android.nfc.action.TRANSACTION_DETECTED"; /** + * Broadcast Action: an RF field ON has been detected. + * @hide + */ + public static final String ACTION_RF_FIELD_ON_DETECTED = + "android.nfc.action.RF_FIELD_ON_DETECTED"; + + /** + * Broadcast Action: an RF Field OFF has been detected. + * @hide + */ + public static final String ACTION_RF_FIELD_OFF_DETECTED = + "android.nfc.action.RF_FIELD_OFF_DETECTED"; + + /** * Broadcast Action: an adapter's state changed between enabled and disabled. * * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains @@ -197,8 +213,7 @@ public final class NfcAdapter { // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort // recovery private static INfcAdapter sService; - - private final Context mContext; + private static INfcTag sTagService; /** * Helper to check if this device has FEATURE_NFC, but without using @@ -235,6 +250,12 @@ public final class NfcAdapter { Log.e(TAG, "could not retrieve NFC service"); return null; } + try { + sTagService = sService.getNfcTagInterface(); + } catch (RemoteException e) { + Log.e(TAG, "could not retrieve NFC Tag service"); + return null; + } } return sService; } @@ -289,7 +310,6 @@ public final class NfcAdapter { if (setupService() == null) { throw new UnsupportedOperationException(); } - mContext = context; } /** @@ -297,10 +317,20 @@ public final class NfcAdapter { * @hide */ public INfcAdapter getService() { + isEnabled(); // NOP call to recover sService if it is stale return sService; } /** + * Returns the binder interface to the tag service. + * @hide + */ + public INfcTag getTagService() { + isEnabled(); // NOP call to recover sTagService if it is stale + return sTagService; + } + + /** * NFC service dead - attempt best effort recovery * @hide */ @@ -309,11 +339,21 @@ public final class NfcAdapter { INfcAdapter service = getServiceInterface(); if (service == null) { Log.e(TAG, "could not retrieve NFC service during service recovery"); + // nothing more can be done now, sService is still stale, we'll hit + // this recovery path again later return; } - /* assigning to sService is not thread-safe, but this is best-effort code - * and on a well-behaved system should never happen */ + // assigning to sService is not thread-safe, but this is best-effort code + // and on a well-behaved system should never happen sService = service; + try { + sTagService = service.getNfcTagInterface(); + } catch (RemoteException ee) { + Log.e(TAG, "could not retrieve NFC tag service during service recovery"); + // nothing more can be done now, sService is still stale, we'll hit + // this recovery path again later + } + return; } @@ -374,6 +414,136 @@ public final class NfcAdapter { } /** + * Enables foreground dispatching to the given Activity. This will force all NFC Intents that + * match the given filters to be delivered to the activity bypassing the standard dispatch + * mechanism. If no IntentFilters are given all the PendingIntent will be invoked for every + * dispatch Intent. + * + * This method must be called from the main thread. + * + * @param activity the Activity to dispatch to + * @param intent the PendingIntent to start for the dispatch + * @param filters the IntentFilters to override dispatching for, or null to always dispatch + * @throws IllegalStateException + */ + public void enableForegroundDispatch(Activity activity, PendingIntent intent, + IntentFilter[] filters, String[][] techLists) { + if (activity == null || intent == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund dispatching can only be enabled " + + "when your activity is resumed"); + } + try { + TechListParcel parcel = null; + if (techLists != null && techLists.length > 0) { + parcel = new TechListParcel(techLists); + } + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + mForegroundDispatchListener); + sService.enableForegroundDispatch(activity.getComponentName(), intent, filters, + parcel); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Disables foreground activity dispatching setup with + * {@link #enableForegroundDispatch}. + * + * <p>This must be called before the Activity returns from + * it's <code>onPause()</code> or this method will throw an IllegalStateException. + * + * <p>This method must be called from the main thread. + */ + public void disableForegroundDispatch(Activity activity) { + ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, + mForegroundDispatchListener); + disableForegroundDispatchInternal(activity, false); + } + + OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() { + @Override + public void onPaused(Activity activity) { + disableForegroundDispatchInternal(activity, true); + } + }; + + void disableForegroundDispatchInternal(Activity activity, boolean force) { + try { + sService.disableForegroundDispatch(activity.getComponentName()); + if (!force && !activity.isResumed()) { + throw new IllegalStateException("You must disable forgeground dispatching " + + "while your activity is still resumed"); + } + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Enable NDEF message push over P2P while this Activity is in the foreground. For this to + * function properly the other NFC device being scanned must support the "com.android.npp" + * NDEF push protocol. + * + * <p><em>NOTE</em> While foreground NDEF push is active standard tag dispatch is disabled. + * Only the foreground activity may receive tag discovered dispatches via + * {@link #enableForegroundDispatch}. + */ + public void enableForegroundNdefPush(Activity activity, NdefMessage msg) { + if (activity == null || msg == null) { + throw new NullPointerException(); + } + if (!activity.isResumed()) { + throw new IllegalStateException("Foregorund NDEF push can only be enabled " + + "when your activity is resumed"); + } + try { + ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, + mForegroundNdefPushListener); + sService.enableForegroundNdefPush(activity.getComponentName(), msg); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** + * Disables foreground NDEF push setup with + * {@link #enableForegroundNdefPush}. + * + * <p>This must be called before the Activity returns from + * it's <code>onPause()</code> or this method will throw an IllegalStateException. + * + * <p>This method must be called from the main thread. + */ + public void disableForegroundNdefPush(Activity activity) { + ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, + mForegroundNdefPushListener); + disableForegroundNdefPushInternal(activity, false); + } + + OnActivityPausedListener mForegroundNdefPushListener = new OnActivityPausedListener() { + @Override + public void onPaused(Activity activity) { + disableForegroundNdefPushInternal(activity, true); + } + }; + + void disableForegroundNdefPushInternal(Activity activity, boolean force) { + try { + sService.disableForegroundNdefPush(activity.getComponentName()); + if (!force && !activity.isResumed()) { + throw new IllegalStateException("You must disable forgeground NDEF push " + + "while your activity is still resumed"); + } + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + } + } + + /** * Set the NDEF Message that this NFC adapter should appear as to Tag * readers. * <p> diff --git a/core/java/android/nfc/NfcSecureElement.java b/core/java/android/nfc/NfcSecureElement.java index 5f4c066..ea2846e 100755 --- a/core/java/android/nfc/NfcSecureElement.java +++ b/core/java/android/nfc/NfcSecureElement.java @@ -16,7 +16,7 @@ package android.nfc; -import android.nfc.technology.TagTechnology; +import android.nfc.tech.TagTechnology; import android.os.RemoteException; import android.util.Log; diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java index 7404950..aae75c9 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -16,36 +16,35 @@ package android.nfc; -import android.nfc.technology.IsoDep; -import android.nfc.technology.MifareClassic; -import android.nfc.technology.MifareUltralight; -import android.nfc.technology.NfcV; -import android.nfc.technology.Ndef; -import android.nfc.technology.NdefFormatable; -import android.nfc.technology.NfcA; -import android.nfc.technology.NfcB; -import android.nfc.technology.NfcF; -import android.nfc.technology.TagTechnology; +import android.nfc.tech.IsoDep; +import android.nfc.tech.MifareClassic; +import android.nfc.tech.MifareUltralight; +import android.nfc.tech.Ndef; +import android.nfc.tech.NdefFormatable; +import android.nfc.tech.NfcA; +import android.nfc.tech.NfcB; +import android.nfc.tech.NfcF; +import android.nfc.tech.NfcV; +import android.nfc.tech.TagTechnology; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import java.util.Arrays; /** * Represents a (generic) discovered tag. * <p> - * A tag is a passive NFC element, such as NFC Forum Tag's, Mifare class Tags, - * Sony Felica Tags. + * A tag is a passive NFC element, such as NFC Forum Tag's, MIFARE class Tags, + * Sony FeliCa Tags, etc. * <p> * Tag's have a type and usually have a UID. * <p> * {@link Tag} objects are passed to applications via the {@link NfcAdapter#EXTRA_TAG} extra * in {@link NfcAdapter#ACTION_TAG_DISCOVERED} intents. A {@link 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 {@link TagTechnology} - * (with {@link Tag#getTechnology(int)}). + * directly queried for its UID and Type, or used to create a {@link TagTechnology} using the + * static <code>get()</code> methods on the varios tech classes. * <p> * A {@link Tag} can be used to create a {@link TagTechnology} only while the tag is in * range. If it is removed and then returned to range, then the most recent @@ -55,13 +54,14 @@ import java.util.Arrays; * time and calls on this class will retrieve those read-only properties, and * not cause any further RF activity or block. Note however that arrays passed to and * returned by this class are *not* cloned, so be careful not to modify them. - * @hide */ public class Tag implements Parcelable { /*package*/ final byte[] mId; /*package*/ final int[] mTechList; + /*package*/ final String[] mTechStringList; /*package*/ final Bundle[] mTechExtras; /*package*/ final int mServiceHandle; // for use by NFC service, 0 indicates a mock + /*package*/ final INfcTag mTagService; /*package*/ int mConnectedTechnology; @@ -69,24 +69,26 @@ public class Tag implements Parcelable { * Hidden constructor to be used by NFC service and internal classes. * @hide */ - public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle) { + public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle, + INfcTag tagService) { if (techList == null) { throw new IllegalArgumentException("rawTargets cannot be null"); } mId = id; mTechList = Arrays.copyOf(techList, techList.length); + mTechStringList = generateTechStringList(techList); // Ensure mTechExtras is as long as mTechList mTechExtras = Arrays.copyOf(techListExtras, techList.length); mServiceHandle = serviceHandle; + mTagService = tagService; mConnectedTechnology = -1; } /** * Construct a mock Tag. - * <p>This is an application constructed tag, so NfcAdapter methods on this - * Tag such as {@link #getTechnology} may fail with - * {@link IllegalArgumentException} since it does not represent a physical Tag. + * <p>This is an application constructed tag, so NfcAdapter methods on this Tag may fail + * with {@link IllegalArgumentException} since it does not represent a physical Tag. * <p>This constructor might be useful for mock testing. * @param id The tag identifier, can be null * @param techList must not be null @@ -94,7 +96,46 @@ public class Tag implements Parcelable { */ public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras) { // set serviceHandle to 0 to indicate mock tag - return new Tag(id, techList, techListExtras, 0); + return new Tag(id, techList, techListExtras, 0, null); + } + + private String[] generateTechStringList(int[] techList) { + final int size = techList.length; + String[] strings = new String[size]; + for (int i = 0; i < size; i++) { + switch (techList[i]) { + case TagTechnology.ISO_DEP: + strings[i] = IsoDep.class.getName(); + break; + case TagTechnology.MIFARE_CLASSIC: + strings[i] = MifareClassic.class.getName(); + break; + case TagTechnology.MIFARE_ULTRALIGHT: + strings[i] = MifareUltralight.class.getName(); + break; + case TagTechnology.NDEF: + strings[i] = Ndef.class.getName(); + break; + case TagTechnology.NDEF_FORMATABLE: + strings[i] = NdefFormatable.class.getName(); + break; + case TagTechnology.NFC_A: + strings[i] = NfcA.class.getName(); + break; + case TagTechnology.NFC_B: + strings[i] = NfcB.class.getName(); + break; + case TagTechnology.NFC_F: + strings[i] = NfcF.class.getName(); + break; + case TagTechnology.NFC_V: + strings[i] = NfcV.class.getName(); + break; + default: + throw new IllegalArgumentException("Unknown tech type " + techList[i]); + } + } + return strings; } /** @@ -119,19 +160,24 @@ public class Tag implements Parcelable { * Returns technologies present in the tag that this implementation understands, * or a zero length array if there are no supported technologies on this tag. * - * The elements of the list are guaranteed be one of the constants defined in - * {@link TagTechnology}. + * The elements of the list are the names of the classes implementing the technology. * * The ordering of the returned array is undefined and should not be relied upon. */ - public int[] getTechnologyList() { - return Arrays.copyOf(mTechList, mTechList.length); + public String[] getTechList() { + return mTechStringList; } - /** - * Returns the technology, or null if not present - */ - public TagTechnology getTechnology(NfcAdapter adapter, int tech) { + /** @hide */ + public boolean hasTech(int techType) { + for (int tech : mTechList) { + if (tech == techType) return true; + } + return false; + } + + /** @hide */ + public Bundle getTechExtras(int tech) { int pos = -1; for (int idx = 0; idx < mTechList.length; idx++) { if (mTechList[idx] == tech) { @@ -143,44 +189,12 @@ public class Tag implements Parcelable { return null; } - Bundle extras = mTechExtras[pos]; - try { - switch (tech) { - case TagTechnology.NFC_A: { - return new NfcA(adapter, this, extras); - } - case TagTechnology.NFC_B: { - return new NfcB(adapter, this, extras); - } - case TagTechnology.ISO_DEP: { - return new IsoDep(adapter, this, extras); - } - case TagTechnology.NFC_V: { - return new NfcV(adapter, this, extras); - } - case TagTechnology.NDEF: { - return new Ndef(adapter, this, tech, extras); - } - case TagTechnology.NDEF_FORMATABLE: { - return new NdefFormatable(adapter, this, tech, extras); - } - case TagTechnology.NFC_F: { - return new NfcF(adapter, this, extras); - } - case TagTechnology.MIFARE_CLASSIC: { - return new MifareClassic(adapter, this, extras); - } - case TagTechnology.MIFARE_ULTRALIGHT: { - return new MifareUltralight(adapter, this, extras); - } + return mTechExtras[pos]; + } - default: { - throw new UnsupportedOperationException("Tech " + tech + " not supported"); - } - } - } catch (RemoteException e) { - return null; - } + /** @hide */ + public INfcTag getTagService() { + return mTagService; } @Override @@ -222,25 +236,41 @@ public class Tag implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + // Null mTagService means this is a mock tag + int isMock = (mTagService == null)?1:0; + writeBytesWithNull(dest, mId); dest.writeInt(mTechList.length); dest.writeIntArray(mTechList); dest.writeTypedArray(mTechExtras, 0); dest.writeInt(mServiceHandle); + dest.writeInt(isMock); + if (isMock == 0) { + dest.writeStrongBinder(mTagService.asBinder()); + } } public static final Parcelable.Creator<Tag> CREATOR = new Parcelable.Creator<Tag>() { @Override public Tag createFromParcel(Parcel in) { + INfcTag tagService; + // Tag fields byte[] id = Tag.readBytesWithNull(in); int[] techList = new int[in.readInt()]; in.readIntArray(techList); Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR); int serviceHandle = in.readInt(); + int isMock = in.readInt(); + if (isMock == 0) { + tagService = INfcTag.Stub.asInterface(in.readStrongBinder()); + } + else { + tagService = null; + } - return new Tag(id, techList, techExtras, serviceHandle); + return new Tag(id, techList, techExtras, serviceHandle, tagService); } @Override @@ -249,7 +279,9 @@ public class Tag implements Parcelable { } }; - /* + /** + * For internal use only. + * * @hide */ public synchronized void setConnectedTechnology(int technology) { @@ -260,14 +292,18 @@ public class Tag implements Parcelable { } } - /* + /** + * For internal use only. + * * @hide */ public int getConnectedTechnology() { return mConnectedTechnology; } - /* + /** + * For internal use only. + * * @hide */ public void setTechnologyDisconnected() { diff --git a/core/java/android/nfc/TagLostException.java b/core/java/android/nfc/TagLostException.java new file mode 100644 index 0000000..1981d7c --- /dev/null +++ b/core/java/android/nfc/TagLostException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc; + +import java.io.IOException; + +public class TagLostException extends IOException { + public TagLostException() { + super(); + } + + public TagLostException(String message) { + super(message); + } +} diff --git a/core/java/android/nfc/TechListParcel.aidl b/core/java/android/nfc/TechListParcel.aidl new file mode 100644 index 0000000..92e646f --- /dev/null +++ b/core/java/android/nfc/TechListParcel.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc; + +parcelable TechListParcel;
\ No newline at end of file diff --git a/core/java/android/nfc/TechListParcel.java b/core/java/android/nfc/TechListParcel.java new file mode 100644 index 0000000..396f0f1 --- /dev/null +++ b/core/java/android/nfc/TechListParcel.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.nfc; + +import android.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class TechListParcel implements Parcelable { + + private String[][] mTechLists; + + public TechListParcel(String[]... strings) { + mTechLists = strings; + } + + public String[][] getTechLists() { + return mTechLists; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + int count = mTechLists.length; + dest.writeInt(count); + for (int i = 0; i < count; i++) { + String[] techList = mTechLists[i]; + dest.writeStringArray(techList); + } + } + + public static final Creator<TechListParcel> CREATOR = new Creator<TechListParcel>() { + @Override + public TechListParcel createFromParcel(Parcel source) { + int count = source.readInt(); + String[][] techLists = new String[count][]; + for (int i = 0; i < count; i++) { + techLists[i] = source.readStringArray(); + } + return new TechListParcel(techLists); + } + + @Override + public TechListParcel[] newArray(int size) { + return new TechListParcel[size]; + } + }; +} diff --git a/core/java/android/nfc/TransceiveResult.aidl b/core/java/android/nfc/TransceiveResult.aidl new file mode 100644 index 0000000..98f92ee --- /dev/null +++ b/core/java/android/nfc/TransceiveResult.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc; + +parcelable TransceiveResult; diff --git a/core/java/android/nfc/TransceiveResult.java b/core/java/android/nfc/TransceiveResult.java new file mode 100644 index 0000000..16244b8 --- /dev/null +++ b/core/java/android/nfc/TransceiveResult.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.nfc; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Class used to pipe transceive result from the NFC service. + * + * @hide + */ +public final class TransceiveResult implements Parcelable { + private final boolean mTagLost; + private final boolean mSuccess; + private final byte[] mResponseData; + + public TransceiveResult(final boolean success, final boolean tagIsLost, + final byte[] data) { + mSuccess = success; + mTagLost = tagIsLost; + mResponseData = data; + } + + public boolean isSuccessful() { + return mSuccess; + } + + public boolean isTagLost() { + return mTagLost; + } + + public byte[] getResponseData() { + return mResponseData; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSuccess ? 1 : 0); + dest.writeInt(mTagLost ? 1 : 0); + if (mSuccess) { + dest.writeInt(mResponseData.length); + dest.writeByteArray(mResponseData); + } + } + + public static final Parcelable.Creator<TransceiveResult> CREATOR = + new Parcelable.Creator<TransceiveResult>() { + @Override + public TransceiveResult createFromParcel(Parcel in) { + boolean success = (in.readInt() == 1) ? true : false; + boolean tagLost = (in.readInt() == 1) ? true : false; + byte[] responseData; + + if (success) { + int responseLength = in.readInt(); + responseData = new byte[responseLength]; + in.readByteArray(responseData); + } else { + responseData = null; + } + return new TransceiveResult(success, tagLost, responseData); + } + + @Override + public TransceiveResult[] newArray(int size) { + return new TransceiveResult[size]; + } + }; + +} diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java new file mode 100644 index 0000000..e635f21 --- /dev/null +++ b/core/java/android/nfc/tech/BasicTagTechnology.java @@ -0,0 +1,160 @@ +/* + * 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.tech; + +import android.nfc.ErrorCodes; +import android.nfc.Tag; +import android.nfc.TagLostException; +import android.nfc.TransceiveResult; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; + +/** + * A base class for tag technologies that are built on top of transceive(). + */ +/* package */ abstract class BasicTagTechnology implements TagTechnology { + private static final String TAG = "NFC"; + + /*package*/ final Tag mTag; + /*package*/ boolean mIsConnected; + /*package*/ int mSelectedTechnology; + + BasicTagTechnology(Tag tag, int tech) throws RemoteException { + mTag = tag; + mSelectedTechnology = tech; + } + + @Override + public Tag getTag() { + return mTag; + } + + /** Internal helper to throw IllegalStateException if the technology isn't connected */ + void checkConnected() { + if ((mTag.getConnectedTechnology() != mSelectedTechnology) || + (mTag.getConnectedTechnology() == -1)) { + throw new IllegalStateException("Call connect() first!"); + } + } + + /** + * Helper to indicate if {@link #connect} has succeeded. + * <p> + * Does not cause RF activity, and does not block. + * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed + * to be within range. Applications must still handle {@link java.io.IOException} + * while using methods that require a connection in case the connection is lost after this + * method returns. + */ + public boolean isConnected() { + if (!mIsConnected) { + return false; + } + + try { + return mTag.getTagService().isPresent(mTag.getServiceHandle()); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + return false; + } + } + + @Override + public void connect() throws IOException { + try { + int errorCode = mTag.getTagService().connect(mTag.getServiceHandle(), + mSelectedTechnology); + + if (errorCode == ErrorCodes.SUCCESS) { + // Store this in the tag object + mTag.setConnectedTechnology(mSelectedTechnology); + mIsConnected = true; + } else { + throw new IOException(); + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + throw new IOException("NFC service died"); + } + } + + @Override + public void reconnect() throws IOException { + if (!mIsConnected) { + throw new IllegalStateException("Technology not connected yet"); + } + + try { + int errorCode = mTag.getTagService().reconnect(mTag.getServiceHandle()); + + if (errorCode != ErrorCodes.SUCCESS) { + mIsConnected = false; + mTag.setTechnologyDisconnected(); + throw new IOException(); + } + } catch (RemoteException e) { + mIsConnected = false; + mTag.setTechnologyDisconnected(); + Log.e(TAG, "NFC service dead", e); + throw new IOException("NFC service died"); + } + } + + @Override + public void close() { + try { + /* Note that we don't want to physically disconnect the tag, + * but just reconnect to it to reset its state + */ + mTag.getTagService().reconnect(mTag.getServiceHandle()); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } finally { + mIsConnected = false; + mTag.setTechnologyDisconnected(); + } + } + + /** Internal transceive */ + /*package*/ byte[] transceive(byte[] data, boolean raw) throws IOException { + checkConnected(); + + try { + TransceiveResult result = mTag.getTagService().transceive(mTag.getServiceHandle(), + data, raw); + if (result == null) { + throw new IOException("transceive failed"); + } else { + if (result.isSuccessful()) { + return result.getResponseData(); + } else { + if (result.isTagLost()) { + throw new TagLostException("Tag was lost."); + } + else { + throw new IOException("transceive failed"); + } + } + } + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + throw new IOException("NFC service died"); + } + } +} diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java new file mode 100644 index 0000000..f6d141a --- /dev/null +++ b/core/java/android/nfc/tech/IsoDep.java @@ -0,0 +1,132 @@ +/* + * 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.tech; + +import android.nfc.Tag; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import java.io.IOException; + +/** + * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as + * ISO1443-4. + * + * <p>You can acquire this kind of connection with {@link #get}. + * Use this class to send and receive data with {@link #transceive transceive()}. + * + * <p>Applications must implement their own protocol stack on top of + * {@link #transceive transceive()}. + * + * <p class="note"><strong>Note:</strong> + * Use of this class requires the {@link android.Manifest.permission#NFC} + * permission. + */ +public final class IsoDep extends BasicTagTechnology { + private static final String TAG = "NFC"; + + /** @hide */ + public static final String EXTRA_HI_LAYER_RESP = "hiresp"; + /** @hide */ + public static final String EXTRA_HIST_BYTES = "histbytes"; + + private byte[] mHiLayerResponse = null; + private byte[] mHistBytes = null; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static IsoDep get(Tag tag) { + if (!tag.hasTech(TagTechnology.ISO_DEP)) return null; + try { + return new IsoDep(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public IsoDep(Tag tag) + throws RemoteException { + super(tag, TagTechnology.ISO_DEP); + Bundle extras = tag.getTechExtras(TagTechnology.ISO_DEP); + if (extras != null) { + mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP); + mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES); + } + } + + /** + * Sets the timeout of an IsoDep transceive transaction in milliseconds. + * If the transaction has not completed before the timeout, + * any ongoing {@link #transceive} operation will be + * aborted and the connection to the tag is lost. This setting is applied + * only to the {@link Tag} object linked to this technology and will be + * reset when {@link IsoDep#close} is called. + * The default transaction timeout is 300 milliseconds. + */ + public void setTimeout(int timeout) { + try { + mTag.getTagService().setIsoDepTimeout(timeout); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + } + + @Override + public void close() { + try { + mTag.getTagService().resetIsoDepTimeout(); + } catch (RemoteException e) { + Log.e(TAG, "NFC service dead", e); + } + super.close(); + } + + /** + * Return the historical bytes if the tag is using {@link NfcA}, null otherwise. + */ + public byte[] getHistoricalBytes() { + return mHistBytes; + } + + /** + * Return the hi layer response bytes if the tag is using {@link NfcB}, null otherwise. + */ + public byte[] getHiLayerResponse() { + return mHiLayerResponse; + } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} 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 { + return transceive(data, true); + } +} diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java new file mode 100644 index 0000000..34fd7cf --- /dev/null +++ b/core/java/android/nfc/tech/MifareClassic.java @@ -0,0 +1,432 @@ +/* + * 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.tech; + +import android.nfc.Tag; +import android.nfc.TagLostException; +import android.os.RemoteException; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Technology class representing MIFARE Classic tags (also known as MIFARE Standard). + * + * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology + * MIFARE Classic tags will still be scanned, but will only show the NfcA technology. + * + * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at + * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption + * built in and each sector has two keys associated with it, as well as ACLs to determine what + * level acess each key grants. Before operating on a sector you must call either + * {@link #authenticateSectorWithKeyA(int, byte[])} or + * {@link #authenticateSectorWithKeyB(int, byte[])} to gain authorization for your request. + */ +public final class MifareClassic extends BasicTagTechnology { + /** + * The well-known default MIFARE read key. All keys are set to this at the factory. + * Using this key will effectively make the payload in the sector public. + */ + public static final byte[] KEY_DEFAULT = + {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; + /** + * The well-known, default MIFARE Application Directory read key. + */ + public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = + {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; + /** + * The well-known, default read key for NDEF data on a MIFARE Classic + */ + public static final byte[] KEY_NFC_FORUM = + {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; + + /** A MIFARE Classic tag */ + public static final int TYPE_CLASSIC = 0; + /** A MIFARE Plus tag */ + public static final int TYPE_PLUS = 1; + /** A MIFARE Pro tag */ + public static final int TYPE_PRO = 2; + /** A Mifare Classic compatible card that does not match the other types */ + public static final int TYPE_OTHER = -1; + + /** The tag contains 16 sectors, each holding 4 blocks. */ + public static final int SIZE_1K = 1024; + /** The tag contains 32 sectors, each holding 4 blocks. */ + public static final int SIZE_2K = 2048; + /** + * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors + * contain 16 blocks. + */ + public static final int SIZE_4K = 4096; + /** The tag contains 5 sectors, each holding 4 blocks. */ + public static final int SIZE_MINI = 320; + + /** Size of a Mifare Classic block (in bytes) */ + public static final int BLOCK_SIZE = 16; + + private static final int MAX_BLOCK_COUNT = 256; + private static final int MAX_SECTOR_COUNT = 40; + + private boolean mIsEmulated; + private int mType; + private int mSize; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static MifareClassic get(Tag tag) { + if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null; + try { + return new MifareClassic(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public MifareClassic(Tag tag) throws RemoteException { + super(tag, TagTechnology.MIFARE_CLASSIC); + + NfcA a = NfcA.get(tag); // Mifare Classic is always based on NFC a + + mIsEmulated = false; + + switch (a.getSak()) { + case 0x08: + mType = TYPE_CLASSIC; + mSize = SIZE_1K; + break; + case 0x09: + mType = TYPE_CLASSIC; + mSize = SIZE_MINI; + break; + case 0x10: + mType = TYPE_PLUS; + mSize = SIZE_2K; + // SecLevel = SL2 + break; + case 0x11: + mType = TYPE_PLUS; + mSize = SIZE_4K; + // Seclevel = SL2 + break; + case 0x18: + mType = TYPE_CLASSIC; + mSize = SIZE_4K; + break; + case 0x28: + mType = TYPE_CLASSIC; + mSize = SIZE_1K; + mIsEmulated = true; + break; + case 0x38: + mType = TYPE_CLASSIC; + mSize = SIZE_4K; + mIsEmulated = true; + break; + case 0x88: + mType = TYPE_CLASSIC; + mSize = SIZE_1K; + // NXP-tag: false + break; + case 0x98: + case 0xB8: + mType = TYPE_PRO; + mSize = SIZE_4K; + break; + default: + // Stack incorrectly reported a MifareClassic. We cannot handle this + // gracefully - we have no idea of the memory layout. Bail. + throw new RuntimeException( + "Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak()); + } + } + + /** Returns the type of the tag, determined at discovery time */ + public int getType() { + return mType; + } + + /** Returns the size of the tag in bytes, determined at discovery time */ + public int getSize() { + return mSize; + } + + /** Returns true if the tag is emulated, determined at discovery time. + * These are actually smart-cards that emulate a Mifare Classic interface. + * They can be treated identically to a Mifare Classic tag. + * @hide + */ + public boolean isEmulated() { + return mIsEmulated; + } + + /** Returns the number of sectors on this tag, determined at discovery time */ + public int getSectorCount() { + switch (mSize) { + case SIZE_1K: + return 16; + case SIZE_2K: + return 32; + case SIZE_4K: + return 40; + case SIZE_MINI: + return 5; + default: + return 0; + } + } + + /** Returns the total block count, determined at discovery time */ + public int getBlockCount() { + return mSize / BLOCK_SIZE; + } + + /** Returns the block count for the given sector, determined at discovery time */ + public int getBlockCountInSector(int sectorIndex) { + validateSector(sectorIndex); + + if (sectorIndex < 32) { + return 4; + } else { + return 16; + } + } + + /** Return the sector index of a given block */ + public int blockToSector(int blockIndex) { + validateBlock(blockIndex); + + if (blockIndex < 32 * 4) { + return blockIndex / 4; + } else { + return 32 + (blockIndex - 32 * 4) / 16; + } + } + + /** Return the first block of a given sector */ + public int sectorToBlock(int sectorIndex) { + if (sectorIndex < 32) { + return sectorIndex * 4; + } else { + return 32 * 4 + (sectorIndex - 32) * 16; + } + } + + // Methods that require connect() + /** + * Authenticate a sector. + * <p>Every sector has an A and B key with different access privileges, + * this method attempts to authenticate against the A key. + * <p>This requires a that the tag be connected. + */ + public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { + return authenticate(sectorIndex, key, true); + } + + /** + * Authenticate a sector. + * <p>Every sector has an A and B key with different access privileges, + * this method attempts to authenticate against the B key. + * <p>This requires a that the tag be connected. + */ + public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException { + return authenticate(sectorIndex, key, false); + } + + private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException { + validateSector(sector); + checkConnected(); + + byte[] cmd = new byte[12]; + + // First byte is the command + if (keyA) { + cmd[0] = 0x60; // phHal_eMifareAuthentA + } else { + cmd[0] = 0x61; // phHal_eMifareAuthentB + } + + // Second byte is block address + // Authenticate command takes a block address. Authenticating a block + // of a sector will authenticate the entire sector. + cmd[1] = (byte) sectorToBlock(sector); + + // Next 4 bytes are last 4 bytes of UID + byte[] uid = getTag().getId(); + System.arraycopy(uid, uid.length - 4, cmd, 2, 4); + + // Next 6 bytes are key + System.arraycopy(key, 0, cmd, 6, 6); + + try { + if (transceive(cmd, false) != null) { + return true; + } + } catch (TagLostException e) { + throw e; + } catch (IOException e) { + // No need to deal with, will return false anyway + } + return false; + } + + /** + * Read 16-byte block. + * <p>This requires a that the tag be connected. + * @throws IOException + */ + public byte[] readBlock(int blockIndex) throws IOException { + validateBlock(blockIndex); + checkConnected(); + + byte[] cmd = { 0x30, (byte) blockIndex }; + return transceive(cmd, false); + } + + /** + * Write 16-byte block. + * <p>This requires a that the tag be connected. + * @throws IOException + */ + public void writeBlock(int blockIndex, byte[] data) throws IOException { + validateBlock(blockIndex); + checkConnected(); + if (data.length != 16) { + throw new IllegalArgumentException("must write 16-bytes"); + } + + byte[] cmd = new byte[data.length + 2]; + cmd[0] = (byte) 0xA0; // MF write command + cmd[1] = (byte) blockIndex; + System.arraycopy(data, 0, cmd, 2, data.length); + + transceive(cmd, false); + } + + /** + * Increment a value block, and store the result in temporary memory. + * @param blockIndex + * @throws IOException + */ + public void increment(int blockIndex, int value) throws IOException { + validateBlock(blockIndex); + validateValueOperand(value); + checkConnected(); + + ByteBuffer cmd = ByteBuffer.allocate(6); + cmd.order(ByteOrder.LITTLE_ENDIAN); + cmd.put( (byte) 0xC1 ); + cmd.put( (byte) blockIndex ); + cmd.putInt(value); + + transceive(cmd.array(), false); + } + + /** + * Decrement a value block, and store the result in temporary memory. + * @param blockIndex + * @throws IOException + */ + public void decrement(int blockIndex, int value) throws IOException { + validateBlock(blockIndex); + validateValueOperand(value); + checkConnected(); + + ByteBuffer cmd = ByteBuffer.allocate(6); + cmd.order(ByteOrder.LITTLE_ENDIAN); + cmd.put( (byte) 0xC0 ); + cmd.put( (byte) blockIndex ); + cmd.putInt(value); + + transceive(cmd.array(), false); + } + + private void validateValueOperand(int value) { + if (value < 0) { + throw new IllegalArgumentException("value operand negative"); + } + } + + /** + * Copy from temporary memory to value block. + * @param blockIndex + * @throws IOException + */ + public void transfer(int blockIndex) throws IOException { + validateBlock(blockIndex); + checkConnected(); + + byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; + + transceive(cmd, false); + } + + /** + * Copy from value block to temporary memory. + * @param blockIndex + * @throws IOException + */ + public void restore(int blockIndex) throws IOException { + validateBlock(blockIndex); + checkConnected(); + + byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; + + transceive(cmd, false); + } + + /** + * Send raw NfcA data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} permission. + * <p>This requires a that the tag be connected. + * + * @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 { + return transceive(data, true); + } + + private void validateSector(int sector) { + // Do not be too strict on upper bounds checking, since some cards + // have more addressable memory than they report. For example, + // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in + // Mifare Classic compatibility mode. + // Note that issuing a command to an out-of-bounds block is safe - the + // tag should report error causing IOException. This validation is a + // helper to guard against obvious programming mistakes. + if (sector < 0 || sector >= MAX_SECTOR_COUNT) { + throw new IndexOutOfBoundsException("sector out of bounds: " + sector); + } + } + + private void validateBlock(int block) { + // Just looking for obvious out of bounds... + if (block < 0 || block >= MAX_BLOCK_COUNT) { + throw new IndexOutOfBoundsException("block out of bounds: " + block); + } + } +} diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java index 7103b4d..f096298 100644 --- a/core/java/android/nfc/technology/MifareUltralight.java +++ b/core/java/android/nfc/tech/MifareUltralight.java @@ -14,120 +14,117 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import java.io.IOException; - -import android.nfc.NfcAdapter; import android.nfc.Tag; -import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** - * Concrete class for TagTechnology.MIFARE_ULTRALIGHT + * Technology class representing MIFARE Ultralight and MIFARE Ultralight C tags. * - * Mifare classic has n sectors, with varying sizes, although - * they are at least the same pattern for any one mifare classic - * product. Each sector has two keys. Authentication with the correct - * key is needed before access to any sector. + * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology + * MIFARE Ultralight class tags will still be scanned, but will only show the NfcA technology. * - * Each sector has k blocks. - * Block size is constant across the whole mifare classic family. + * <p>MIFARE Ultralight class tags have a series of 4 bytes pages that can be individually written + * and read in chunks of 4 for a total read of 16 bytes. */ public final class MifareUltralight extends BasicTagTechnology { + /** A MIFARE Ultralight tag */ public static final int TYPE_ULTRALIGHT = 1; + /** A MIFARE Ultralight C tag */ public static final int TYPE_ULTRALIGHT_C = 2; + /** The tag type is unknown */ public static final int TYPE_UNKNOWN = 10; private static final int NXP_MANUFACTURER_ID = 0x04; private int mType; - public MifareUltralight(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException { - super(adapter, tag, TagTechnology.MIFARE_ULTRALIGHT); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static MifareUltralight get(Tag tag) { + if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null; + try { + return new MifareUltralight(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public MifareUltralight(Tag tag) throws RemoteException { + super(tag, TagTechnology.MIFARE_ULTRALIGHT); // Check if this could actually be a Mifare - NfcA a = (NfcA) tag.getTechnology(adapter, TagTechnology.NFC_A); + NfcA a = NfcA.get(tag); mType = TYPE_UNKNOWN; - if( a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID ) { + if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) { // could be UL or UL-C mType = TYPE_ULTRALIGHT; } } + /** Returns the type of the tag */ public int getType() { return mType; } // Methods that require connect() /** + * Reads a single 16 byte block from the given page offset. + * + * <p>This requires a that the tag be connected. + * * @throws IOException */ - public byte[] readBlock(int block) throws IOException { + public byte[] readBlock(int page) throws IOException { checkConnected(); - byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead - return transceive(blockread_cmd); + byte[] blockread_cmd = { 0x30, (byte) page}; // phHal_eMifareRead + return transceive(blockread_cmd, false); } /** + * Writes a 4 byte page to the tag. + * + * <p>This requires a that the tag be connected. + * + * @param page The offset of the page to write + * @param data The data to write * @throws IOException */ - public byte[] readOTP() throws IOException { - checkConnected(); - - return readBlock(3); // OTP is at page 3 - } - - public void writePage(int block, byte[] data) throws IOException { + public void writePage(int page, byte[] data) throws IOException { checkConnected(); byte[] pagewrite_cmd = new byte[data.length + 2]; pagewrite_cmd[0] = (byte) 0xA2; - pagewrite_cmd[1] = (byte) block; + pagewrite_cmd[1] = (byte) page; System.arraycopy(data, 0, pagewrite_cmd, 2, data.length); - transceive(pagewrite_cmd); - } - - public void writeBlock(int block, byte[] data) throws IOException { - checkConnected(); - - byte[] blockwrite_cmd = new byte[data.length + 2]; - blockwrite_cmd[0] = (byte) 0xA0; - blockwrite_cmd[1] = (byte) block; - System.arraycopy(data, 0, blockwrite_cmd, 2, data.length); - - transceive(blockwrite_cmd); + transceive(pagewrite_cmd, false); } /** - * Send data to a tag and receive the response. + * Send raw NfcA data to a tag and receive the response. * <p> * This method will block until the response is received. It can be canceled * with {@link #close}. * <p>Requires {@link android.Manifest.permission#NFC} permission. + * <p>This requires a that the tag be connected. * * @param data bytes to send * @return bytes received in response * @throws IOException if the target is lost or connection closed */ - @Override public byte[] transceive(byte[] data) throws IOException { - checkConnected(); - - try { - byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false); - if (response == null) { - throw new IOException("transceive failed"); - } - return response; - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } + return transceive(data, true); } - } diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/tech/Ndef.java index 05872fe..c6804f9 100644 --- a/core/java/android/nfc/technology/Ndef.java +++ b/core/java/android/nfc/tech/Ndef.java @@ -14,30 +14,34 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; import android.nfc.ErrorCodes; import android.nfc.FormatException; +import android.nfc.INfcTag; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; /** * A high-level connection to a {@link Tag} using one of the NFC type 1, 2, 3, or 4 technologies * to interact with NDEF data. MiFare Classic cards that present NDEF data may also be used - * via this class. To determine the exact technology being used call {@link #getTechnologyId()} + * via this class. To determine the exact technology being used call {@link #getType()} * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * * <p class="note"><strong>Note:</strong> * Use of this class requires the {@link android.Manifest.permission#NFC} * permission. */ public final class Ndef extends BasicTagTechnology { + private static final String TAG = "NFC"; + /** @hide */ public static final int NDEF_MODE_READ_ONLY = 1; /** @hide */ @@ -57,14 +61,12 @@ public final class Ndef extends BasicTagTechnology { /** @hide */ public static final String EXTRA_NDEF_TYPE = "ndeftype"; - //TODO: consider removing OTHER entirely - and not allowing Ndef to - // enumerate for tag types outside of (NFC Forum 1-4, MifareClassic) public static final int OTHER = -1; public static final int NFC_FORUM_TYPE_1 = 1; public static final int NFC_FORUM_TYPE_2 = 2; public static final int NFC_FORUM_TYPE_3 = 3; public static final int NFC_FORUM_TYPE_4 = 4; - public static final int MIFARE_CLASSIC = 105; + public static final int MIFARE_CLASSIC = 101; private final int mMaxNdefSize; private final int mCardState; @@ -72,11 +74,27 @@ public final class Ndef extends BasicTagTechnology { private final int mNdefType; /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static Ndef get(Tag tag) { + if (!tag.hasTech(TagTechnology.NDEF)) return null; + try { + return new Ndef(tag); + } catch (RemoteException e) { + return null; + } + } + + /** * Internal constructor, to be used by NfcAdapter * @hide */ - public Ndef(NfcAdapter adapter, Tag tag, int tech, Bundle extras) throws RemoteException { - super(adapter, tag, tech); + public Ndef(Tag tag) throws RemoteException { + super(tag, TagTechnology.NDEF); + Bundle extras = tag.getTechExtras(TagTechnology.NDEF); if (extras != null) { mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH); mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE); @@ -97,15 +115,6 @@ public final class Ndef extends BasicTagTechnology { } /** - * Get optional extra NDEF messages. - * Some tags may contain extra NDEF messages, but not all - * implementations will be able to read them. - */ - public NdefMessage[] getExtraNdefMessage() throws IOException, FormatException { - throw new UnsupportedOperationException(); - } - - /** * Get NDEF tag type. * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2}, * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4}, @@ -148,11 +157,12 @@ public final class Ndef extends BasicTagTechnology { checkConnected(); try { + INfcTag tagService = mTag.getTagService(); int serviceHandle = mTag.getServiceHandle(); - if (mTagService.isNdef(serviceHandle)) { - NdefMessage msg = mTagService.ndefRead(serviceHandle); + if (tagService.isNdef(serviceHandle)) { + NdefMessage msg = tagService.ndefRead(serviceHandle); if (msg == null) { - int errorCode = mTagService.getLastError(serviceHandle); + int errorCode = tagService.getLastError(serviceHandle); switch (errorCode) { case ErrorCodes.ERROR_IO: throw new IOException(); @@ -168,7 +178,7 @@ public final class Ndef extends BasicTagTechnology { return null; } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); return null; } } @@ -181,9 +191,10 @@ public final class Ndef extends BasicTagTechnology { checkConnected(); try { + INfcTag tagService = mTag.getTagService(); int serviceHandle = mTag.getServiceHandle(); - if (mTagService.isNdef(serviceHandle)) { - int errorCode = mTagService.ndefWrite(serviceHandle, msg); + if (tagService.isNdef(serviceHandle)) { + int errorCode = tagService.ndefWrite(serviceHandle, msg); switch (errorCode) { case ErrorCodes.SUCCESS: break; @@ -200,67 +211,54 @@ public final class Ndef extends BasicTagTechnology { throw new IOException("Tag is not ndef"); } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); } } /** - * Attempt to write extra NDEF messages. - * Implementations may be able to write extra NDEF - * message after the first primary message, but it is not - * guaranteed. Even if it can be written, other implementations - * may not be able to read NDEF messages after the primary message. - * It is recommended to use additional NDEF records instead. - * - * @throws IOException + * Indicates whether a tag can be made read-only with + * {@link #makeReadonly()} */ - public void writeExtraNdefMessage(int i, NdefMessage msg) throws IOException, FormatException { - checkConnected(); - - throw new UnsupportedOperationException(); + public boolean canMakeReadonly() { + if (mNdefType == NFC_FORUM_TYPE_1 || mNdefType == NFC_FORUM_TYPE_2) { + return true; + } else { + return false; + } } /** - * Set the CC field to indicate this tag is read-only + * Sets the CC field to indicate this tag is read-only + * and permanently sets the lock bits to prevent any further NDEF + * modifications. + * This is a one-way process and can not be reverted! * @throws IOException */ public boolean makeReadonly() throws IOException { checkConnected(); try { - int errorCode = mTagService.ndefMakeReadOnly(mTag.getServiceHandle()); - 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(); - } + INfcTag tagService = mTag.getTagService(); + if (tagService.isNdef(mTag.getServiceHandle())) { + int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle()); + 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(); + } + } + else { + throw new IOException("Tag is not ndef"); + } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); return false; } } - - /** - * Attempt to use tag specific technology to really make - * the tag read-only - * For NFC Forum Type 1 and 2 only. - */ - public void makeLowLevelReadonly() { - checkConnected(); - - throw new UnsupportedOperationException(); - } - - @Override - public byte[] transceive(byte[] data) { - checkConnected(); - - throw new UnsupportedOperationException(); - } } diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java index 222c558..2919c43 100644 --- a/core/java/android/nfc/technology/NdefFormatable.java +++ b/core/java/android/nfc/tech/NdefFormatable.java @@ -14,34 +14,52 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; import android.nfc.ErrorCodes; import android.nfc.FormatException; +import android.nfc.INfcTag; import android.nfc.NdefMessage; import android.nfc.NfcAdapter; import android.nfc.Tag; -import android.os.Bundle; import android.os.RemoteException; +import android.util.Log; import java.io.IOException; /** * An interface to a {@link Tag} allowing to format the tag as NDEF. * - * <p>You can acquire this kind of interface with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * * <p class="note"><strong>Note:</strong> * Use of this class requires the {@link android.Manifest.permission#NFC} * permission. */ public final class NdefFormatable extends BasicTagTechnology { + private static final String TAG = "NFC"; + + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NdefFormatable get(Tag tag) { + if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null; + try { + return new NdefFormatable(tag); + } catch (RemoteException e) { + return null; + } + } + /** * Internal constructor, to be used by NfcAdapter * @hide */ - public NdefFormatable(NfcAdapter adapter, Tag tag, int tech, Bundle extras) throws RemoteException { - super(adapter, tag, tech); + public NdefFormatable(Tag tag) throws RemoteException { + super(tag, TagTechnology.NDEF_FORMATABLE); } /** @@ -52,10 +70,9 @@ public final class NdefFormatable extends BasicTagTechnology { checkConnected(); try { - byte[] DEFAULT_KEY = {(byte)0xFF,(byte)0xFF,(byte)0xFF, - (byte)0xFF,(byte)0xFF,(byte)0xFF}; int serviceHandle = mTag.getServiceHandle(); - int errorCode = mTagService.formatNdef(serviceHandle, DEFAULT_KEY); + INfcTag tagService = mTag.getTagService(); + int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT); switch (errorCode) { case ErrorCodes.SUCCESS: break; @@ -68,8 +85,8 @@ public final class NdefFormatable extends BasicTagTechnology { throw new IOException(); } // Now check and see if the format worked - if (mTagService.isNdef(serviceHandle)) { - errorCode = mTagService.ndefWrite(serviceHandle, firstMessage); + if (tagService.isNdef(serviceHandle)) { + errorCode = tagService.ndefWrite(serviceHandle, firstMessage); switch (errorCode) { case ErrorCodes.SUCCESS: break; @@ -85,14 +102,7 @@ public final class NdefFormatable extends BasicTagTechnology { throw new IOException(); } } catch (RemoteException e) { - attemptDeadServiceRecovery(e); + Log.e(TAG, "NFC service dead", e); } } - - @Override - public byte[] transceive(byte[] data) { - checkConnected(); - - throw new UnsupportedOperationException(); - } } diff --git a/core/java/android/nfc/technology/NfcA.java b/core/java/android/nfc/tech/NfcA.java index ef46762..24badc4 100644 --- a/core/java/android/nfc/technology/NfcA.java +++ b/core/java/android/nfc/tech/NfcA.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** * A low-level connection to a {@link Tag} using the NFC-A technology, also known as * ISO1443-3A. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -44,8 +45,25 @@ public final class NfcA extends BasicTagTechnology { private short mSak; private byte[] mAtqa; - public NfcA(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException { - super(adapter, tag, TagTechnology.NFC_A); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcA get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_A)) return null; + try { + return new NfcA(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcA(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_A); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_A); mSak = extras.getShort(EXTRA_SAK); mAtqa = extras.getByteArray(EXTRA_ATQA); } @@ -63,4 +81,19 @@ public final class NfcA extends BasicTagTechnology { public short getSak() { return mSak; } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} 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 { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/technology/NfcB.java b/core/java/android/nfc/tech/NfcB.java index 267c94d..abeef32 100644 --- a/core/java/android/nfc/technology/NfcB.java +++ b/core/java/android/nfc/tech/NfcB.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** * A low-level connection to a {@link Tag} using the NFC-B technology, also known as * ISO1443-3B. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -44,9 +45,25 @@ public final class NfcB extends BasicTagTechnology { private byte[] mAppData; private byte[] mProtInfo; - public NfcB(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.NFC_B); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcB get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_B)) return null; + try { + return new NfcB(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcB(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_B); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_B); mAppData = extras.getByteArray(EXTRA_APPDATA); mProtInfo = extras.getByteArray(EXTRA_PROTINFO); } @@ -67,4 +84,18 @@ public final class NfcB extends BasicTagTechnology { return mProtInfo; } + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} 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 { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/technology/NfcF.java b/core/java/android/nfc/tech/NfcF.java index 6741ac8..f617739 100644 --- a/core/java/android/nfc/technology/NfcF.java +++ b/core/java/android/nfc/tech/NfcF.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** * A low-level connection to a {@link Tag} using the NFC-F technology, also known as * JIS6319-4. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -44,9 +45,25 @@ public final class NfcF extends BasicTagTechnology { private byte[] mSystemCode = null; private byte[] mManufacturer = null; - public NfcF(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.NFC_F); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcF get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_F)) return null; + try { + return new NfcF(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcF(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_F); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_F); if (extras != null) { mSystemCode = extras.getByteArray(EXTRA_SC); mManufacturer = extras.getByteArray(EXTRA_PMM); @@ -60,4 +77,19 @@ public final class NfcF extends BasicTagTechnology { public byte[] getManufacturer() { return mManufacturer; } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} 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 { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/technology/NfcV.java b/core/java/android/nfc/tech/NfcV.java index 460de6a..8e1f066 100644 --- a/core/java/android/nfc/technology/NfcV.java +++ b/core/java/android/nfc/tech/NfcV.java @@ -14,18 +14,19 @@ * limitations under the License. */ -package android.nfc.technology; +package android.nfc.tech; -import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.os.RemoteException; +import java.io.IOException; + /** - * A low-level connection to a {@link Tag} using the NFC-V technology, also known as + * A low-level connection to a {@link Tag} using NFC vicinity technology, also known as * ISO15693. * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. + * <p>You can acquire this kind of connection with {@link #get}. * Use this class to send and receive data with {@link #transceive transceive()}. * * <p>Applications must implement their own protocol stack on top of @@ -45,9 +46,25 @@ public final class NfcV extends BasicTagTechnology { private byte mRespFlags; private byte mDsfId; - public NfcV(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.NFC_V); + /** + * Returns an instance of this tech for the given tag. If the tag doesn't support + * this tech type null is returned. + * + * @param tag The tag to get the tech from + */ + public static NfcV get(Tag tag) { + if (!tag.hasTech(TagTechnology.NFC_V)) return null; + try { + return new NfcV(tag); + } catch (RemoteException e) { + return null; + } + } + + /** @hide */ + public NfcV(Tag tag) throws RemoteException { + super(tag, TagTechnology.NFC_V); + Bundle extras = tag.getTechExtras(TagTechnology.NFC_V); mRespFlags = extras.getByte(EXTRA_RESP_FLAGS); mDsfId = extras.getByte(EXTRA_DSFID); } @@ -59,4 +76,19 @@ public final class NfcV extends BasicTagTechnology { public byte getDsfId() { return mDsfId; } + + /** + * Send data to a tag and receive the response. + * <p> + * This method will block until the response is received. It can be canceled + * with {@link #close}. + * <p>Requires {@link android.Manifest.permission#NFC} 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 { + return transceive(data, true); + } } diff --git a/core/java/android/nfc/tech/TagTechnology.java b/core/java/android/nfc/tech/TagTechnology.java new file mode 100644 index 0000000..aebb3e8 --- /dev/null +++ b/core/java/android/nfc/tech/TagTechnology.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.tech; + +import android.nfc.Tag; + +import java.io.IOException; + +public interface TagTechnology { + /** + * This technology is an instance of {@link NfcA}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_A = 1; + + /** + * This technology is an instance of {@link NfcB}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_B = 2; + + /** + * This technology is an instance of {@link IsoDep}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int ISO_DEP = 3; + + /** + * This technology is an instance of {@link NfcF}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_F = 4; + + /** + * This technology is an instance of {@link NfcV}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NFC_V = 5; + + /** + * This technology is an instance of {@link Ndef}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NDEF = 6; + + /** + * This technology is an instance of {@link NdefFormatable}. + * <p>Support for this technology type is mandatory. + * @hide + */ + public static final int NDEF_FORMATABLE = 7; + + /** + * This technology is an instance of {@link MifareClassic}. + * <p>Support for this technology type is optional. If a stack doesn't support this technology + * type tags using it must still be discovered and present the lower level radio interface + * technologies in use. + * @hide + */ + public static final int MIFARE_CLASSIC = 8; + + /** + * This technology is an instance of {@link MifareUltralight}. + * <p>Support for this technology type is optional. If a stack doesn't support this technology + * type tags using it must still be discovered and present the lower level radio interface + * technologies in use. + * @hide + */ + public static final int MIFARE_ULTRALIGHT = 9; + + /** + * Get the {@link Tag} object this technology came from. + */ + public Tag getTag(); + + /** + * Opens a connection to the {@link Tag} enabling interactive commands. The command set + * varies by the technology type. + * + * <p>This method blocks until the connection has been established. + * + * <p>A call to {@link #close} from another thread will cancel a blocked call and cause an + * IOException to be thrown on the thread that is blocked. + * + * @see #reconnect() + * @see #close() + * @throws IOException if the target is lost, or connect canceled + */ + public void connect() throws IOException; + + /** + * Re-connect to the {@link Tag} associated with this connection. Reconnecting to a tag can be + * used to reset the state of the tag itself. + * + * <p>This method blocks until the connection is re-established. + * + * <p>A call to {@link #close} from another thread will cancel a blocked call and cause an + * IOException to be thrown on the thread that is blocked. + * + * @see #connect() + * @see #close() + * @throws IOException + */ + public void reconnect() throws IOException; + + /** + * Closes the connection to the {@link Tag}. This call is non-blocking and causes all blocking + * operations such as {@link #connect} to be canceled and immediately throw + * {@link java.io.IOException} on the thread that is blocked. + * + * <p> + * Once this method is called, this object cannot be re-used and should be discarded. Further + * calls to {@link #connect} will fail. + * + * @see #connect() + * @see #reconnect() + */ + public void close(); +} diff --git a/core/java/android/nfc/technology/BasicTagTechnology.java b/core/java/android/nfc/technology/BasicTagTechnology.java deleted file mode 100644 index 553f6ec..0000000 --- a/core/java/android/nfc/technology/BasicTagTechnology.java +++ /dev/null @@ -1,254 +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 android.nfc.technology; - -import java.io.IOException; - -import android.nfc.INfcAdapter; -import android.nfc.INfcTag; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.ErrorCodes; -import android.os.RemoteException; -import android.util.Log; - -/** - * A base class for tag technologies that are built on top of transceive(). - */ -/* package */ abstract class BasicTagTechnology implements TagTechnology { - - /*package*/ final Tag mTag; - /*package*/ boolean mIsConnected; - /*package*/ int mSelectedTechnology; - private final NfcAdapter mAdapter; - - // Following fields are final after construction, except for - // during attemptDeadServiceRecovery() when NFC crashes. - // Not locked - we accept a best effort attempt when NFC crashes. - /*package*/ INfcAdapter mService; - /*package*/ INfcTag mTagService; - - private static final String TAG = "NFC"; - - /** - * @hide - */ - public BasicTagTechnology(NfcAdapter adapter, Tag tag, int tech) throws RemoteException { - int[] techList = tag.getTechnologyList(); - int i; - - // Check target validity - for (i = 0; i < techList.length; i++) { - if (tech == techList[i]) { - break; - } - } - if (i >= techList.length) { - // Technology not found - throw new IllegalArgumentException("Technology " + tech + " not present on tag " + tag); - } - - mAdapter = adapter; - mService = mAdapter.getService(); - try { - mTagService = mService.getNfcTagInterface(); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - } - mTag = tag; - mSelectedTechnology = tech; - } - - /** - * @hide - */ - public BasicTagTechnology(NfcAdapter adapter, Tag tag) throws RemoteException { - this(adapter, tag, tag.getTechnologyList()[0]); - } - - /** NFC service dead - attempt best effort recovery */ - /*package*/ void attemptDeadServiceRecovery(Exception e) { - mAdapter.attemptDeadServiceRecovery(e); - /* assigning to mService is not thread-safe, but this is best-effort code - * and on a well-behaved system should never happen */ - mService = mAdapter.getService(); - try { - mTagService = mService.getNfcTagInterface(); - } catch (RemoteException e2) { - Log.e(TAG, "second RemoteException trying to recover from dead NFC service", e2); - } - } - - /** - * Get the {@link Tag} this connection is associated with. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - */ - @Override - public Tag getTag() { - return mTag; - } - - public void checkConnected() { - if ((mTag.getConnectedTechnology() != getTechnologyId()) || - (mTag.getConnectedTechnology() == -1)) { - throw new IllegalStateException("Call connect() first!"); - } - } - - /** - * <p>Requires {@link android.Manifest.permission#NFC} permission. - */ - @Override - public int getTechnologyId() { - return mSelectedTechnology; - } - - /** - * Helper to indicate if {@link #transceive transceive()} calls might succeed. - * <p> - * Does not cause RF activity, and does not block. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed - * to be within range. Applications must still handle {@link java.io.IOException} - * while using {@link #transceive transceive()}, in case connection is lost after this method - * returns true. - */ - public boolean isConnected() { - if (!mIsConnected) { - return false; - } - - try { - return mTagService.isPresent(mTag.getServiceHandle()); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - return false; - } - } - - /** - * Connect to the {@link Tag} associated with this connection. - * <p> - * This method blocks until the connection is established. - * <p> - * {@link #close} can be called from another thread to cancel this connection - * attempt. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * @throws IOException if the target is lost, or connect canceled - */ - @Override - public void connect() throws IOException { - try { - int errorCode = mTagService.connect(mTag.getServiceHandle(), getTechnologyId()); - - if (errorCode == ErrorCodes.SUCCESS) { - // Store this in the tag object - mTag.setConnectedTechnology(getTechnologyId()); - mIsConnected = true; - } else { - throw new IOException(); - } - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } - - /** - * Re-connect to the {@link Tag} associated with this connection. - * <p> - * Reconnecting to a tag can be used to reset the state of the tag itself. - * This method blocks until the connection is re-established. - * <p> - * {@link #close} can be called from another thread to cancel this connection - * attempt. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * @throws IOException if the target is lost, or connect canceled - */ - @Override - public void reconnect() throws IOException { - if (!mIsConnected) { - throw new IllegalStateException("Technology not connected yet"); - } else { - try { - int errorCode = mTagService.reconnect(mTag.getServiceHandle()); - - if (errorCode != ErrorCodes.SUCCESS) { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - throw new IOException(); - } - } catch (RemoteException e) { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } - } - - /** - * Close this connection. - * <p> - * Causes blocking operations such as {@link #transceive transceive()} or {@link #connect} to - * be canceled and immediately throw {@link java.io.IOException}. - * <p> - * Once this method is called, this object cannot be re-used and should be discarded. Further - * calls to {@link #transceive transceive()} or {@link #connect} will fail. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - */ - @Override - public void close() { - try { - /* Note that we don't want to physically disconnect the tag, - * but just reconnect to it to reset its state - */ - mTagService.reconnect(mTag.getServiceHandle()); - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - } finally { - mIsConnected = false; - mTag.setTechnologyDisconnected(); - } - } - - /** - * Send data to a tag and receive the response. - * <p> - * This method will block until the response is received. It can be canceled - * with {@link #close}. - * <p>Requires {@link android.Manifest.permission#NFC} 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 { - checkConnected(); - - try { - byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, true); - if (response == null) { - throw new IOException("transceive failed"); - } - return response; - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } -} diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java deleted file mode 100644 index 32a7542..0000000 --- a/core/java/android/nfc/technology/IsoDep.java +++ /dev/null @@ -1,67 +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 android.nfc.technology; - -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import java.io.IOException; - -/** - * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as - * ISO1443-4. - * - * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}. - * Use this class to send and receive data with {@link #transceive transceive()}. - * - * <p>Applications must implement their own protocol stack on top of - * {@link #transceive transceive()}. - * - * <p class="note"><strong>Note:</strong> - * Use of this class requires the {@link android.Manifest.permission#NFC} - * permission. - */ -public final class IsoDep extends BasicTagTechnology { - /** @hide */ - public static final String EXTRA_HI_LAYER_RESP = "hiresp"; - /** @hide */ - public static final String EXTRA_HIST_BYTES = "histbytes"; - - private byte[] mHiLayerResponse = null; - private byte[] mHistBytes = null; - - public IsoDep(NfcAdapter adapter, Tag tag, Bundle extras) - throws RemoteException { - super(adapter, tag, TagTechnology.ISO_DEP); - if (extras != null) { - mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP); - mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES); - } - } - - /** - * 3A only - */ - public byte[] getHistoricalBytes() { return mHistBytes; } - - /** - * 3B only - */ - public byte[] getHiLayerResponse() { return mHiLayerResponse; } -} diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java deleted file mode 100644 index 799f0a7..0000000 --- a/core/java/android/nfc/technology/MifareClassic.java +++ /dev/null @@ -1,404 +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 android.nfc.technology; - -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.os.RemoteException; - -import java.io.IOException; - -/** - * Concrete class for TagTechnology.MIFARE_CLASSIC - * - * Mifare classic has n sectors, with varying sizes, although - * they are at least the same pattern for any one mifare classic - * product. Each sector has two keys. Authentication with the correct - * key is needed before access to any sector. - * - * Each sector has k blocks. - * Block size is constant across the whole mifare classic family. - */ -public final class MifareClassic extends BasicTagTechnology { - /** - * The well-known, default MIFARE read key. - * Use this key to effectively make the payload in this sector - * public. - */ - public static final byte[] KEY_DEFAULT = - {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; - /** - * The well-known, default Mifare Application Directory read key. - */ - public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = - {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; - /** - * The well-known, default read key for NDEF data on a Mifare Classic - */ - public static final byte[] KEY_NFC_FORUM = - {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; - - public static final int TYPE_CLASSIC = 0; - public static final int TYPE_PLUS = 1; - public static final int TYPE_PRO = 2; - public static final int TYPE_DESFIRE = 3; - public static final int TYPE_ULTRALIGHT = 4; - public static final int TYPE_UNKNOWN = 5; - - public static final int SIZE_1K = 1024; - public static final int SIZE_2K = 2048; - public static final int SIZE_4K = 4096; - public static final int SIZE_MINI = 320; - public static final int SIZE_UNKNOWN = 0; - - private boolean mIsEmulated; - private int mType; - private int mSize; - - public MifareClassic(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException { - super(adapter, tag, TagTechnology.MIFARE_CLASSIC); - - // Check if this could actually be a Mifare - NfcA a = (NfcA) tag.getTechnology(adapter, TagTechnology.NFC_A); - //short[] ATQA = getATQA(tag); - - mIsEmulated = false; - mType = TYPE_UNKNOWN; - mSize = SIZE_UNKNOWN; - - switch (a.getSak()) { - case 0x00: - // could be UL or UL-C - mType = TYPE_ULTRALIGHT; - break; - case 0x08: - // Type == classic - // Size = 1K - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - break; - case 0x09: - // Type == classic mini - // Size == ? - mType = TYPE_CLASSIC; - mSize = SIZE_MINI; - break; - case 0x10: - // Type == MF+ - // Size == 2K - // SecLevel = SL2 - mType = TYPE_PLUS; - mSize = SIZE_2K; - break; - case 0x11: - // Type == MF+ - // Size == 4K - // Seclevel = SL2 - mType = TYPE_PLUS; - mSize = SIZE_4K; - break; - case 0x18: - // Type == classic - // Size == 4k - mType = TYPE_CLASSIC; - mSize = SIZE_4K; - break; - case 0x20: - // TODO this really should be a short, not byte - if (a.getAtqa()[0] == 0x03) { - // Type == DESFIRE - mType = TYPE_DESFIRE; - } else { - // Type == MF+ - // SL = SL3 - mType = TYPE_PLUS; - mSize = SIZE_UNKNOWN; - } - break; - case 0x28: - // Type == MF Classic - // Size == 1K - // Emulated == true - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - mIsEmulated = true; - break; - case 0x38: - // Type == MF Classic - // Size == 4K - // Emulated == true - mType = TYPE_CLASSIC; - mSize = SIZE_4K; - mIsEmulated = true; - break; - case 0x88: - // Type == MF Classic - // Size == 1K - // NXP-tag: false - mType = TYPE_CLASSIC; - mSize = SIZE_1K; - break; - case 0x98: - case 0xB8: - // Type == MF Pro - // Size == 4K - mType = TYPE_PRO; - mSize = SIZE_4K; - break; - default: - // Unknown mifare - mType = TYPE_UNKNOWN; - mSize = SIZE_UNKNOWN; - break; - } - } - - // Immutable data known at discovery time - public int getSize() { - return mSize; - } - - public int getType() { - return mType; - } - - public boolean isEmulated() { - return mIsEmulated; - } - - public int getSectorCount() { - switch (mSize) { - case SIZE_1K: { - return 16; - } - case SIZE_2K: { - return 32; - } - case SIZE_4K: { - return 40; - } - case SIZE_MINI: { - return 5; - } - default: { - return 0; - } - } - } - - public int getSectorSize(int sector) { - return getBlockCount(sector) * 16; - } - - public int getTotalBlockCount() { - int totalBlocks = 0; - for (int sec = 0; sec < getSectorCount(); sec++) { - totalBlocks += getSectorSize(sec); - } - - return totalBlocks; - } - - public int getBlockCount(int sector) { - if (sector >= getSectorCount()) { - throw new IllegalArgumentException("this card only has " + getSectorCount() + - " sectors"); - } - - if (sector <= 32) { - return 4; - } else { - return 16; - } - } - - private byte firstBlockInSector(int sector) { - if (sector < 32) { - return (byte) ((sector * 4) & 0xff); - } else { - return (byte) ((32 * 4 + ((sector - 32) * 16)) & 0xff); - } - } - - // Methods that require connect() - /** - * Authenticate for a given block. - * Note that this will authenticate the entire sector the block belongs to. - */ - public boolean authenticateBlock(int block, byte[] key, boolean keyA) { - checkConnected(); - - byte[] cmd = new byte[12]; - - // First byte is the command - if (keyA) { - cmd[0] = 0x60; // phHal_eMifareAuthentA - } else { - cmd[0] = 0x61; // phHal_eMifareAuthentB - } - - // Second byte is block address - cmd[1] = (byte) block; - - // Next 4 bytes are last 4 bytes of UID - byte[] uid = getTag().getId(); - System.arraycopy(uid, uid.length - 4, cmd, 2, 4); - - // Next 6 bytes are key - System.arraycopy(key, 0, cmd, 6, 6); - - try { - if ((transceive(cmd) != null)) { - return true; - } - } catch (IOException e) { - // No need to deal with, will return false anyway - } - return false; - } - - /** - * Authenticate for a given sector. - */ - public boolean authenticateSector(int sector, byte[] key, boolean keyA) { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector)) & 0xff); - - // Note that authenticating a block of a sector, will authenticate - // the entire sector. - return authenticateBlock(addr, key, keyA); - } - - /** - * Sector indexing starts at 0. - * Block indexing starts at 0, and resets in each sector. - * @throws IOException - */ - public byte[] readBlock(int sector, int block) throws IOException { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); - return readBlock(addr); - - } - - /** - * Reads absolute block index. - * @throws IOException - */ - public byte[] readBlock(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] blockread_cmd = { 0x30, addr }; - - return transceive(blockread_cmd); - } - - /** - * Writes absolute block index. - * @throws IOException - */ - public void writeBlock(int block, byte[] data) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] blockwrite_cmd = new byte[data.length + 2]; - blockwrite_cmd[0] = (byte) 0xA0; // MF write command - blockwrite_cmd[1] = addr; - System.arraycopy(data, 0, blockwrite_cmd, 2, data.length); - - transceive(blockwrite_cmd); - } - - /** - * Writes relative block in sector. - * @throws IOException - */ - public void writeBlock(int sector, int block, byte[] data) throws IOException { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); - - writeBlock(addr, data); - } - - public void increment(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] incr_cmd = { (byte) 0xC1, (byte) block }; - - transceive(incr_cmd); - } - - public void decrement(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] decr_cmd = { (byte) 0xC0, (byte) block }; - - transceive(decr_cmd); - } - - public void transfer(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] trans_cmd = { (byte) 0xB0, (byte) block }; - - transceive(trans_cmd); - } - - public void restore(int block) throws IOException { - checkConnected(); - - byte addr = (byte) block; - byte[] rest_cmd = { (byte) 0xC2, (byte) block }; - - transceive(rest_cmd); - } - - /** - * Send data to a tag and receive the response. - * <p> - * This method will block until the response is received. It can be canceled - * with {@link #close}. - * <p>Requires {@link android.Manifest.permission#NFC} permission. - * - * @param data bytes to send - * @return bytes received in response - * @throws IOException if the target is lost or connection closed - */ - @Override - public byte[] transceive(byte[] data) throws IOException { - checkConnected(); - - try { - byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false); - if (response == null) { - throw new IOException("transceive failed"); - } - return response; - } catch (RemoteException e) { - attemptDeadServiceRecovery(e); - throw new IOException("NFC service died"); - } - } -} diff --git a/core/java/android/nfc/technology/TagTechnology.java b/core/java/android/nfc/technology/TagTechnology.java deleted file mode 100644 index 62216c1..0000000 --- a/core/java/android/nfc/technology/TagTechnology.java +++ /dev/null @@ -1,94 +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 android.nfc.technology; - -import android.nfc.Tag; - -import java.io.IOException; - -public interface TagTechnology { - /** - * This object is an instance of {@link NfcA} - */ - public static final int NFC_A = 1; - - /** - * This object is an instance of {@link NfcB} - */ - public static final int NFC_B = 2; - - /** - * This object is an instance of {@link IsoDep} - */ - public static final int ISO_DEP = 3; - - /** - * This object is an instance of {@link NfcF} - */ - public static final int NFC_F = 4; - - /** - * This object is an instance of {@link NfcV} - */ - public static final int NFC_V = 5; - - /** - * This object is an instance of {@link Ndef} - */ - public static final int NDEF = 6; - - /** - * This object is an instance of {@link NdefFormatable} - */ - public static final int NDEF_FORMATABLE = 7; - - /** - * This object is an instance of {@link MifareClassic} - */ - public static final int MIFARE_CLASSIC = 8; - - /** - * This object is an instance of {@link MifareUltralight} - */ - public static final int MIFARE_ULTRALIGHT = 9; - - /** - * Returns the technology type for this tag connection. - */ - public int getTechnologyId(); - - /** - * Get the backing tag object. - */ - public Tag getTag(); - - /** - * @throws IOException - */ - public void connect() throws IOException; - - /** - * @throws IOException - */ - public void reconnect() throws IOException; - - /** - * Non-blocking. Immediately causes all blocking calls - * to throw IOException. - */ - public void close(); -} diff --git a/core/java/android/nfc/technology/package.html b/core/java/android/nfc/technology/package.html deleted file mode 100644 index 26b8a32..0000000 --- a/core/java/android/nfc/technology/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<HTML> -<BODY> -{@hide} -</BODY> -</HTML>
\ No newline at end of file diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index eb941e4..557e53f 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -96,9 +96,9 @@ public final class Message implements Parcelable { // sometimes we store linked lists of these things /*package*/ Message next; - private static Object mPoolSync = new Object(); - private static Message mPool; - private static int mPoolSize = 0; + private static final Object sPoolSync = new Object(); + private static Message sPool; + private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 10; @@ -107,11 +107,12 @@ public final class Message implements Parcelable { * avoid allocating new objects in many cases. */ public static Message obtain() { - synchronized (mPoolSync) { - if (mPool != null) { - Message m = mPool; - mPool = m.next; + synchronized (sPoolSync) { + if (sPool != null) { + Message m = sPool; + sPool = m.next; m.next = null; + sPoolSize--; return m; } } @@ -248,12 +249,12 @@ public final class Message implements Parcelable { * freed. */ public void recycle() { - synchronized (mPoolSync) { - if (mPoolSize < MAX_POOL_SIZE) { + synchronized (sPoolSync) { + if (sPoolSize < MAX_POOL_SIZE) { clearForRecycle(); - - next = mPool; - mPool = this; + next = sPool; + sPool = this; + sPoolSize++; } } } diff --git a/core/java/android/os/storage/IObbActionListener.java b/core/java/android/os/storage/IObbActionListener.java index 35da4b0..d6afbaa 100644 --- a/core/java/android/os/storage/IObbActionListener.java +++ b/core/java/android/os/storage/IObbActionListener.java @@ -113,7 +113,7 @@ public interface IObbActionListener extends IInterface { _data.writeInt(nonce); _data.writeInt(status); mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, - android.os.IBinder.FLAG_ONEWAY); + IBinder.FLAG_ONEWAY); _reply.readException(); } finally { _reply.recycle(); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 44887ed..95d985d 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -517,8 +517,11 @@ public abstract class WallpaperService extends Service { mLayout.windowAnimations = com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); - mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets, - mInputChannel); + if (mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets, + mInputChannel) < 0) { + Log.w(TAG, "Failed to add window while updating wallpaper surface."); + return; + } mCreated = true; InputQueue.registerInputChannel(mInputChannel, mInputHandler, diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index c46d2c5..6d7b7ce 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -512,10 +512,17 @@ public class TextToSpeech { Intent intent = new Intent("android.intent.action.START_TTS_SERVICE"); intent.addCategory("android.intent.category.TTS"); - mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE); - // TODO handle case where the binding works (should always work) but - // the plugin fails + boolean bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + if (!bound) { + Log.e("TextToSpeech.java", "initTts() failed to bind to service"); + if (mInitListener != null) { + mInitListener.onInit(ERROR); + } + } else { + // initialization listener will be called inside ServiceConnection + Log.i("TextToSpeech.java", "initTts() successfully bound to service"); + } + // TODO handle plugin failures } @@ -765,8 +772,9 @@ public class TextToSpeech { { synchronized (mStartLock) { int result = ERROR; - Log.i("TTS", "speak() queueMode=" + queueMode); + Log.i("TextToSpeech.java - speak", "speak text of length " + text.length()); if (!mStarted) { + Log.e("TextToSpeech.java - speak", "service isn't started"); return result; } try { @@ -1264,10 +1272,13 @@ public class TextToSpeech { */ public int synthesizeToFile(String text, HashMap<String,String> params, String filename) { - Log.i("TTS", "synthesizeToFile()"); + Log.i("TextToSpeech.java", "synthesizeToFile()"); synchronized (mStartLock) { int result = ERROR; + Log.i("TextToSpeech.java - synthesizeToFile", "synthesizeToFile text of length " + + text.length()); if (!mStarted) { + Log.e("TextToSpeech.java - synthesizeToFile", "service isn't started"); return result; } try { diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index d24af52..6b44f9e 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -38,7 +38,7 @@ import java.util.HashMap; * for the device you are running on. For example: * * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService - * Context.LAYOUT_INFLATER_SERVICE);</pre> + * (Context.LAYOUT_INFLATER_SERVICE);</pre> * * <p> * To create a new LayoutInflater with an additional {@link Factory} for your diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c26fa93..02e5b63 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1171,14 +1171,6 @@ public interface WindowManager extends ViewManager { gravity = o.gravity; changes |= LAYOUT_CHANGED; } - if (horizontalMargin != o.horizontalMargin) { - horizontalMargin = o.horizontalMargin; - changes |= LAYOUT_CHANGED; - } - if (verticalMargin != o.verticalMargin) { - verticalMargin = o.verticalMargin; - changes |= LAYOUT_CHANGED; - } if (format != o.format) { format = o.format; changes |= FORMAT_CHANGED; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 38ac37d..70f90d3 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -50,6 +50,6 @@ public class ChooserActivity extends ResolverActivity { initialIntents[i] = (Intent)pa[i]; } } - super.onCreate(savedInstanceState, target, title, initialIntents, false); + super.onCreate(savedInstanceState, target, title, initialIntents, null, false); } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 215e9ae..841de06 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -63,11 +63,12 @@ public class ResolverActivity extends AlertActivity implements protected void onCreate(Bundle savedInstanceState) { onCreate(savedInstanceState, new Intent(getIntent()), getResources().getText(com.android.internal.R.string.whichApplication), - null, true); + null, null, true); } protected void onCreate(Bundle savedInstanceState, Intent intent, - CharSequence title, Intent[] initialIntents, boolean alwaysUseOption) { + CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList, + boolean alwaysUseOption) { super.onCreate(savedInstanceState); mPm = getPackageManager(); intent.setComponent(null); @@ -88,7 +89,7 @@ public class ResolverActivity extends AlertActivity implements com.android.internal.R.id.clearDefaultHint); mClearDefaultHint.setVisibility(View.GONE); } - mAdapter = new ResolveListAdapter(this, intent, initialIntents); + mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList); if (mAdapter.getCount() > 1) { ap.mAdapter = mAdapter; } else if (mAdapter.getCount() == 1) { @@ -215,14 +216,16 @@ public class ResolverActivity extends AlertActivity implements private List<DisplayResolveInfo> mList; public ResolveListAdapter(Context context, Intent intent, - Intent[] initialIntents) { + Intent[] initialIntents, List<ResolveInfo> rList) { mIntent = new Intent(intent); mIntent.setComponent(null); mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - List<ResolveInfo> rList = mPm.queryIntentActivities( - intent, PackageManager.MATCH_DEFAULT_ONLY - | (mAlwaysCheck != null ? PackageManager.GET_RESOLVED_FILTER : 0)); + if (rList == null) { + rList = mPm.queryIntentActivities( + intent, PackageManager.MATCH_DEFAULT_ONLY + | (mAlwaysCheck != null ? PackageManager.GET_RESOLVED_FILTER : 0)); + } int N; if ((rList != null) && ((N = rList.size()) > 0)) { // Only display the first matches that are either of equal diff --git a/core/java/com/android/internal/nfc/LlcpSocket.java b/core/java/com/android/internal/nfc/LlcpSocket.java index 73c0925..63888ae 100644 --- a/core/java/com/android/internal/nfc/LlcpSocket.java +++ b/core/java/com/android/internal/nfc/LlcpSocket.java @@ -193,7 +193,7 @@ public class LlcpSocket { throw new IOException(); } } catch (RemoteException e) { - Log.e(TAG, "RemoteException in send(): ", e); + Log.e(TAG, "RemoteException in receive(): ", e); } return receivedLength; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b682947..b3b80f6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -4087,7 +4087,7 @@ public final class BatteryStatsImpl extends BatteryStats { // we have gone through a significant charge (from a very low // level to a now very high level). if (oldStatus == BatteryManager.BATTERY_STATUS_FULL - || level >= 95 + || level >= 90 || (mDischargeCurrentLevel < 20 && level >= 80)) { doWrite = true; resetAllStatsLocked(); diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java index bc749d8..0885b6e 100644 --- a/core/java/com/android/internal/widget/DigitalClock.java +++ b/core/java/com/android/internal/widget/DigitalClock.java @@ -34,6 +34,7 @@ import android.view.View; import android.widget.RelativeLayout; import android.widget.TextView; +import java.lang.ref.WeakReference; import java.text.DateFormatSymbols; import java.util.Calendar; @@ -54,26 +55,45 @@ public class DigitalClock extends RelativeLayout { private TextView mTimeDisplayForeground; private AmPm mAmPm; private ContentObserver mFormatChangeObserver; - private boolean mLive = true; - private boolean mAttached; + private int mAttached = 0; // for debugging - tells us whether attach/detach is unbalanced /* called by system on minute ticks */ private final Handler mHandler = new Handler(); - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (mLive && intent.getAction().equals( - Intent.ACTION_TIMEZONE_CHANGED)) { - mCalendar = Calendar.getInstance(); - } - // Post a runnable to avoid blocking the broadcast. - mHandler.post(new Runnable() { - public void run() { - updateTime(); + private BroadcastReceiver mIntentReceiver; + + private static class TimeChangedReceiver extends BroadcastReceiver { + private WeakReference<DigitalClock> mClock; + private Context mContext; + + public TimeChangedReceiver(DigitalClock clock) { + mClock = new WeakReference<DigitalClock>(clock); + mContext = clock.getContext(); + } + + @Override + public void onReceive(Context context, Intent intent) { + // Post a runnable to avoid blocking the broadcast. + final boolean timezoneChanged = + intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED); + final DigitalClock clock = mClock.get(); + if (clock != null) { + clock.mHandler.post(new Runnable() { + public void run() { + if (timezoneChanged) { + clock.mCalendar = Calendar.getInstance(); } + clock.updateTime(); + } }); + } else { + try { + mContext.unregisterReceiver(this); + } catch (RuntimeException e) { + // Shouldn't happen + } } - }; + } + }; static class AmPm { private TextView mAmPm; @@ -99,14 +119,27 @@ public class DigitalClock extends RelativeLayout { } } - private class FormatChangeObserver extends ContentObserver { - public FormatChangeObserver() { + private static class FormatChangeObserver extends ContentObserver { + private WeakReference<DigitalClock> mClock; + private Context mContext; + public FormatChangeObserver(DigitalClock clock) { super(new Handler()); + mClock = new WeakReference<DigitalClock>(clock); + mContext = clock.getContext(); } @Override public void onChange(boolean selfChange) { - setDateFormat(); - updateTime(); + DigitalClock digitalClock = mClock.get(); + if (digitalClock != null) { + digitalClock.setDateFormat(); + digitalClock.updateTime(); + } else { + try { + mContext.getContentResolver().unregisterContentObserver(this); + } catch (RuntimeException e) { + // Shouldn't happen + } + } } } @@ -139,11 +172,11 @@ public class DigitalClock extends RelativeLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (mAttached) return; - mAttached = true; + mAttached++; - if (mLive) { - /* monitor time ticks, time changed, timezone */ + /* monitor time ticks, time changed, timezone */ + if (mIntentReceiver == null) { + mIntentReceiver = new TimeChangedReceiver(this); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); @@ -152,9 +185,11 @@ public class DigitalClock extends RelativeLayout { } /* monitor 12/24-hour display preference */ - mFormatChangeObserver = new FormatChangeObserver(); - mContext.getContentResolver().registerContentObserver( - Settings.System.CONTENT_URI, true, mFormatChangeObserver); + if (mFormatChangeObserver == null) { + mFormatChangeObserver = new FormatChangeObserver(this); + mContext.getContentResolver().registerContentObserver( + Settings.System.CONTENT_URI, true, mFormatChangeObserver); + } updateTime(); } @@ -163,16 +198,19 @@ public class DigitalClock extends RelativeLayout { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (!mAttached) return; - mAttached = false; + mAttached--; - if (mLive) { + if (mIntentReceiver != null) { mContext.unregisterReceiver(mIntentReceiver); } - mContext.getContentResolver().unregisterContentObserver( - mFormatChangeObserver); - } + if (mFormatChangeObserver != null) { + mContext.getContentResolver().unregisterContentObserver( + mFormatChangeObserver); + } + mFormatChangeObserver = null; + mIntentReceiver = null; + } void updateTime(Calendar c) { mCalendar = c; @@ -180,9 +218,7 @@ public class DigitalClock extends RelativeLayout { } private void updateTime() { - if (mLive) { - mCalendar.setTimeInMillis(System.currentTimeMillis()); - } + mCalendar.setTimeInMillis(System.currentTimeMillis()); CharSequence newTime = DateFormat.format(mFormat, mCalendar); mTimeDisplayBackground.setText(newTime); @@ -195,8 +231,4 @@ public class DigitalClock extends RelativeLayout { ? M24 : M12; mAmPm.setShowAmPm(mFormat.equals(M12)); } - - void setLive(boolean live) { - mLive = live; - } } diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java new file mode 100644 index 0000000..2b6dee8 --- /dev/null +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -0,0 +1,41 @@ +/* + * 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.content; + +import android.content.ContentResolver; +import android.provider.ContactsContract; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +public class ContentResolverTest extends AndroidTestCase { + private ContentResolver mContentResolver; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContentResolver = mContext.getContentResolver(); + } + + @LargeTest + public void testCursorFinalizer() throws Exception { + // TODO: Want a test case that more predictably reproduce this issue. Selected + // 600 as this causes the problem 100% of the runs on current hw, it might not + // do so on some other configuration though. + for (int i = 0; i < 600; i++) { + mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); + } + } +} |