diff options
38 files changed, 1419 insertions, 726 deletions
diff --git a/api/current.xml b/api/current.xml index 2a2200d..5fd7a1b 100644 --- a/api/current.xml +++ b/api/current.xml @@ -131704,9 +131704,97 @@ > <parameter name="path" type="java.lang.String"> </parameter> -<parameter name="state" type="java.lang.String"> +<parameter name="state" type="int"> </parameter> </method> +<field name="ERROR_ALREADY_MOUNTED" + type="int" + transient="false" + volatile="false" + value="24" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_COULD_NOT_MOUNT" + type="int" + transient="false" + volatile="false" + value="21" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_COULD_NOT_UNMOUNT" + type="int" + transient="false" + volatile="false" + value="22" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_INTERNAL" + type="int" + transient="false" + volatile="false" + value="20" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_NOT_MOUNTED" + type="int" + transient="false" + volatile="false" + value="23" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_PERMISSION_DENIED" + type="int" + transient="false" + volatile="false" + value="25" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="MOUNTED" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="UNMOUNTED" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="StorageManager" extends="java.lang.Object" @@ -131741,8 +131829,6 @@ > <parameter name="filename" type="java.lang.String"> </parameter> -<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> -</exception> </method> <method name="mountObb" return="boolean" diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 9494a06..c185007 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -104,6 +104,10 @@ public final class StrictMode { // Only show an annoying dialog at most every 30 seconds private static final long MIN_DIALOG_INTERVAL_MS = 30000; + // How many offending stacks to keep track of (and time) per loop + // of the Looper. + private static final int MAX_OFFENSES_PER_LOOP = 10; + // Thread-policy: /** @@ -680,6 +684,17 @@ public final class StrictMode { } } + private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed = + new ThreadLocal<ArrayList<ViolationInfo>>() { + @Override protected ArrayList<ViolationInfo> initialValue() { + return new ArrayList<ViolationInfo>(); + } + }; + + private static boolean tooManyViolationsThisLoop() { + return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP; + } + private static class AndroidBlockGuardPolicy implements BlockGuard.Policy { private int mPolicyMask; @@ -707,6 +722,9 @@ public final class StrictMode { if ((mPolicyMask & DETECT_DISK_WRITE) == 0) { return; } + if (tooManyViolationsThisLoop()) { + return; + } BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask); e.fillInStackTrace(); startHandlingViolationException(e); @@ -717,6 +735,9 @@ public final class StrictMode { if ((mPolicyMask & DETECT_DISK_READ) == 0) { return; } + if (tooManyViolationsThisLoop()) { + return; + } BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask); e.fillInStackTrace(); startHandlingViolationException(e); @@ -727,6 +748,9 @@ public final class StrictMode { if ((mPolicyMask & DETECT_NETWORK) == 0) { return; } + if (tooManyViolationsThisLoop()) { + return; + } BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask); e.fillInStackTrace(); startHandlingViolationException(e); @@ -747,13 +771,6 @@ public final class StrictMode { handleViolationWithTimingAttempt(info); } - private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed = - new ThreadLocal<ArrayList<ViolationInfo>>() { - @Override protected ArrayList<ViolationInfo> initialValue() { - return new ArrayList<ViolationInfo>(); - } - }; - // Attempts to fill in the provided ViolationInfo's // durationMillis field if this thread has a Looper we can use // to measure with. We measure from the time of violation @@ -780,7 +797,7 @@ public final class StrictMode { MessageQueue queue = Looper.myQueue(); final ArrayList<ViolationInfo> records = violationsBeingTimed.get(); - if (records.size() >= 10) { + if (records.size() >= MAX_OFFENSES_PER_LOOP) { // Not worth measuring. Too many offenses in one loop. return; } diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 60ea95c..467a0ac 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -484,7 +484,7 @@ public interface IMountService extends IInterface { * IObbActionListener to inform it of the terminal state of the * call. */ - public void mountObb(String filename, String key, IObbActionListener token) + public void mountObb(String filename, String key, IObbActionListener token, int nonce) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -493,6 +493,7 @@ public interface IMountService extends IInterface { _data.writeString(filename); _data.writeString(key); _data.writeStrongBinder((token != null ? token.asBinder() : null)); + _data.writeInt(nonce); mRemote.transact(Stub.TRANSACTION_mountObb, _data, _reply, 0); _reply.readException(); } finally { @@ -508,8 +509,8 @@ public interface IMountService extends IInterface { * IObbActionListener to inform it of the terminal state of the * call. */ - public void unmountObb(String filename, boolean force, IObbActionListener token) - throws RemoteException { + public void unmountObb(String filename, boolean force, IObbActionListener token, + int nonce) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { @@ -517,6 +518,7 @@ public interface IMountService extends IInterface { _data.writeString(filename); _data.writeInt((force ? 1 : 0)); _data.writeStrongBinder((token != null ? token.asBinder() : null)); + _data.writeInt(nonce); mRemote.transact(Stub.TRANSACTION_unmountObb, _data, _reply, 0); _reply.readException(); } finally { @@ -855,7 +857,9 @@ public interface IMountService extends IInterface { key = data.readString(); IObbActionListener observer; observer = IObbActionListener.Stub.asInterface(data.readStrongBinder()); - mountObb(filename, key, observer); + int nonce; + nonce = data.readInt(); + mountObb(filename, key, observer, nonce); reply.writeNoException(); return true; } @@ -867,7 +871,9 @@ public interface IMountService extends IInterface { force = 0 != data.readInt(); IObbActionListener observer; observer = IObbActionListener.Stub.asInterface(data.readStrongBinder()); - unmountObb(filename, force, observer); + int nonce; + nonce = data.readInt(); + unmountObb(filename, force, observer, nonce); reply.writeNoException(); return true; } @@ -979,7 +985,7 @@ public interface IMountService extends IInterface { * MountService will call back to the supplied IObbActionListener to inform * it of the terminal state of the call. */ - public void mountObb(String filename, String key, IObbActionListener token) + public void mountObb(String filename, String key, IObbActionListener token, int nonce) throws RemoteException; /* @@ -1023,7 +1029,7 @@ public interface IMountService extends IInterface { * MountService will call back to the supplied IObbActionListener to inform * it of the terminal state of the call. */ - public void unmountObb(String filename, boolean force, IObbActionListener token) + public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce) throws RemoteException; /* diff --git a/core/java/android/os/storage/IObbActionListener.java b/core/java/android/os/storage/IObbActionListener.java index 2c098ac..d6fa58a 100644 --- a/core/java/android/os/storage/IObbActionListener.java +++ b/core/java/android/os/storage/IObbActionListener.java @@ -69,9 +69,11 @@ public interface IObbActionListener extends IInterface { data.enforceInterface(DESCRIPTOR); String filename; filename = data.readString(); - String status; - status = data.readString(); - this.onObbResult(filename, status); + int nonce; + nonce = data.readInt(); + int status; + status = data.readInt(); + this.onObbResult(filename, nonce, status); reply.writeNoException(); return true; } @@ -101,13 +103,15 @@ public interface IObbActionListener extends IInterface { * on * @param returnCode status of the operation */ - public void onObbResult(String filename, String status) throws RemoteException { + public void onObbResult(String filename, int nonce, int status) + throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(filename); - _data.writeString(status); + _data.writeInt(nonce); + _data.writeInt(status); mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, 0); _reply.readException(); } finally { @@ -124,7 +128,8 @@ public interface IObbActionListener extends IInterface { * Return from an OBB action result. * * @param filename the path to the OBB the operation was performed on - * @param returnCode status of the operation + * @param nonce identifier that is meaningful to the receiver + * @param status status code as defined in {@link OnObbStateChangeListener} */ - public void onObbResult(String filename, String status) throws RemoteException; + public void onObbResult(String filename, int nonce, int status) throws RemoteException; } diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java index a2d0a56..950195b 100644 --- a/core/java/android/os/storage/OnObbStateChangeListener.java +++ b/core/java/android/os/storage/OnObbStateChangeListener.java @@ -17,15 +17,69 @@ package android.os.storage; /** - * Used for receiving notifications from {@link StorageManager}. + * Used for receiving notifications from {@link StorageManager} about OBB file + * states. */ public abstract class OnObbStateChangeListener { + + /** + * The OBB container is now mounted and ready for use. Returned in status + * messages from calls made via {@link StorageManager} + */ + public static final int MOUNTED = 1; + + /** + * The OBB container is now unmounted and not usable. Returned in status + * messages from calls made via {@link StorageManager} + */ + public static final int UNMOUNTED = 2; + + /** + * There was an internal system error encountered while trying to mount the + * OBB. Returned in status messages from calls made via + * {@link StorageManager} + */ + public static final int ERROR_INTERNAL = 20; + + /** + * The OBB could not be mounted by the system. Returned in status messages + * from calls made via {@link StorageManager} + */ + public static final int ERROR_COULD_NOT_MOUNT = 21; + + /** + * The OBB could not be unmounted. This most likely indicates that a file is + * in use on the OBB. Returned in status messages from calls made via + * {@link StorageManager} + */ + public static final int ERROR_COULD_NOT_UNMOUNT = 22; + + /** + * A call was made to unmount the OBB when it was not mounted. Returned in + * status messages from calls made via {@link StorageManager} + */ + public static final int ERROR_NOT_MOUNTED = 23; + + /** + * The OBB has already been mounted. Returned in status messages from calls + * made via {@link StorageManager} + */ + public static final int ERROR_ALREADY_MOUNTED = 24; + + /** + * The current application does not have permission to use this OBB because + * the OBB indicates it's owned by a different package or the key used to + * open it is incorrect. Returned in status messages from calls made via + * {@link StorageManager} + */ + public static final int ERROR_PERMISSION_DENIED = 25; + /** * Called when an OBB has changed states. * * @param path path to the OBB file the state change has happened on * @param state the current state of the OBB */ - public void onObbStateChange(String path, String state) { + public void onObbStateChange(String path, int state) { } } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 2ebd049..73ac79f 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -22,12 +22,13 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * StorageManager is the interface to the systems storage service. The storage @@ -69,7 +70,12 @@ public class StorageManager /* * List of our listeners */ - private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>(); + private List<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>(); + + /* + * Next available nonce + */ + final private AtomicInteger mNextNonce = new AtomicInteger(0); private class MountServiceBinderListener extends IMountServiceListener.Stub { public void onUsbMassStorageConnectionChanged(boolean available) { @@ -93,57 +99,38 @@ public class StorageManager private final ObbActionListener mObbActionListener = new ObbActionListener(); private class ObbActionListener extends IObbActionListener.Stub { - private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>(); + private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); @Override - public void onObbResult(String filename, String status) throws RemoteException { + public void onObbResult(String filename, int nonce, int status) throws RemoteException { + final ObbListenerDelegate delegate; synchronized (mListeners) { - final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator(); - while (iter.hasNext()) { - final WeakReference<ObbListenerDelegate> ref = iter.next(); - - final ObbListenerDelegate delegate = (ref == null) ? null : ref.get(); - if (delegate == null) { - iter.remove(); - continue; - } - - delegate.sendObbStateChanged(filename, status); + delegate = mListeners.get(nonce); + if (delegate != null) { + mListeners.remove(nonce); } } - } - public void addListener(OnObbStateChangeListener listener) { - if (listener == null) { - return; + if (delegate != null) { + delegate.sendObbStateChanged(filename, status); } + } - synchronized (mListeners) { - final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator(); - while (iter.hasNext()) { - final WeakReference<ObbListenerDelegate> ref = iter.next(); - - final ObbListenerDelegate delegate = (ref == null) ? null : ref.get(); - if (delegate == null) { - iter.remove(); - continue; - } - - /* - * If we're already in the listeners, we don't need to be in - * there again. - */ - if (listener.equals(delegate.getListener())) { - return; - } - } + public int addListener(OnObbStateChangeListener listener) { + final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); - final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); - mListeners.add(new WeakReference<ObbListenerDelegate>(delegate)); + synchronized (mListeners) { + mListeners.put(delegate.nonce, delegate); } + + return delegate.nonce; } } + private int getNextNonce() { + return mNextNonce.getAndIncrement(); + } + /** * Private class containing sender and receiver code for StorageEvents. */ @@ -151,7 +138,10 @@ public class StorageManager private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; private final Handler mHandler; + private final int nonce; + ObbListenerDelegate(OnObbStateChangeListener listener) { + nonce = getNextNonce(); mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); mHandler = new Handler(mTgtLooper) { @Override @@ -180,7 +170,7 @@ public class StorageManager return mObbEventListenerRef.get(); } - void sendObbStateChanged(String path, String state) { + void sendObbStateChanged(String path, int state) { ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state); mHandler.sendMessage(e.getMessage()); } @@ -191,9 +181,10 @@ public class StorageManager */ private class ObbStateChangedStorageEvent extends StorageEvent { public final String path; - public final String state; - public ObbStateChangedStorageEvent(String path, String state) { + public final int state; + + public ObbStateChangedStorageEvent(String path, int state) { super(EVENT_OBB_STATE_CHANGED); this.path = path; this.state = state; @@ -420,10 +411,8 @@ public class StorageManager * <p> * The OBB will remain mounted for as long as the StorageManager reference * is held by the application. As soon as this reference is lost, the OBBs - * in use will be unmounted. The {@link OnObbStateChangeListener} registered with - * this call will receive all further OBB-related events until it goes out - * of scope. If the caller is not interested in whether the call succeeds, - * the <code>listener</code> may be specified as <code>null</code>. + * in use will be unmounted. The {@link OnObbStateChangeListener} registered + * with this call will receive the success or failure of this operation. * <p> * <em>Note:</em> you can only mount OBB files for which the OBB tag on the * file matches a package ID that is owned by the calling program's UID. @@ -433,12 +422,21 @@ public class StorageManager * @param filename the path to the OBB file * @param key secret used to encrypt the OBB; may be <code>null</code> if no * encryption was used on the OBB. + * @param listener will receive the success or failure of the operation * @return whether the mount call was successfully queued or not */ public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } + + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + try { - mObbActionListener.addListener(listener); - mMountService.mountObb(filename, key, mObbActionListener); + final int nonce = mObbActionListener.addListener(listener); + mMountService.mountObb(filename, key, mObbActionListener, nonce); return true; } catch (RemoteException e) { Log.e(TAG, "Failed to mount OBB", e); @@ -452,10 +450,8 @@ public class StorageManager * <code>force</code> flag is true, it will kill any application needed to * unmount the given OBB (even the calling application). * <p> - * The {@link OnObbStateChangeListener} registered with this call will receive all - * further OBB-related events until it goes out of scope. If the caller is - * not interested in whether the call succeeded, the listener may be - * specified as <code>null</code>. + * The {@link OnObbStateChangeListener} registered with this call will + * receive the success or failure of this operation. * <p> * <em>Note:</em> you can only mount OBB files for which the OBB tag on the * file matches a package ID that is owned by the calling program's UID. @@ -466,12 +462,21 @@ public class StorageManager * @param filename path to the OBB file * @param force whether to kill any programs using this in order to unmount * it + * @param listener will receive the success or failure of the operation * @return whether the unmount call was successfully queued or not */ public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } + + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + try { - mObbActionListener.addListener(listener); - mMountService.unmountObb(filename, force, mObbActionListener); + final int nonce = mObbActionListener.addListener(listener); + mMountService.unmountObb(filename, force, mObbActionListener, nonce); return true; } catch (RemoteException e) { Log.e(TAG, "Failed to mount OBB", e); @@ -486,7 +491,11 @@ public class StorageManager * @param filename path to OBB image * @return true if OBB is mounted; false if not mounted or on error */ - public boolean isObbMounted(String filename) throws IllegalArgumentException { + public boolean isObbMounted(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } + try { return mMountService.isObbMounted(filename); } catch (RemoteException e) { @@ -506,12 +515,14 @@ public class StorageManager * not mounted or exception encountered trying to read status */ public String getMountedObbPath(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } + try { return mMountService.getMountedObbPath(filename); } catch (RemoteException e) { Log.e(TAG, "Failed to find mounted path for OBB", e); - } catch (IllegalArgumentException e) { - Log.d(TAG, "Couldn't read OBB file", e); } return null; diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index ef7716e..1fd31a3 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -77,6 +77,7 @@ public interface WindowManagerPolicy { public final static int FLAG_VIRTUAL = 0x00000100; public final static int FLAG_INJECTED = 0x01000000; + public final static int FLAG_TRUSTED = 0x02000000; public final static int FLAG_WOKE_HERE = 0x10000000; public final static int FLAG_BRIGHT_HERE = 0x20000000; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9d914ad..43eeda8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1332,10 +1332,6 @@ <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> - <activity android:name="com.android.internal.app.ExternalMediaFormatActivity" - android:theme="@style/Theme.Dialog.Alert" - android:excludeFromRecents="true"> - </activity> <activity android:name="android.accounts.ChooseAccountActivity" android:excludeFromRecents="true" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 340e23c..c3cbf07 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -148,6 +148,9 @@ <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> + + <!-- If this is true, the screen will come on when you unplug usb/power/whatever. --> + <bool name="config_unplugTurnsOnScreen">false</bool> <!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION --> diff --git a/core/tests/coretests/src/com/android/server/MountServiceTests.java b/core/tests/coretests/src/com/android/server/MountServiceTests.java index 83e9d18..1f8c92e 100644 --- a/core/tests/coretests/src/com/android/server/MountServiceTests.java +++ b/core/tests/coretests/src/com/android/server/MountServiceTests.java @@ -21,7 +21,6 @@ import com.android.frameworks.coretests.R; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; -import android.os.Environment; import android.os.FileUtils; import android.os.storage.OnObbStateChangeListener; import android.os.storage.StorageManager; @@ -57,17 +56,15 @@ public class MountServiceTests extends AndroidTestCase { } } - private interface CompletableTask { - public boolean isDone(); - } + private static class ObbObserver extends OnObbStateChangeListener { + private String path; - private static class ObbObserver extends OnObbStateChangeListener implements CompletableTask { - public String path; - public String state; + public int state = -1; boolean done = false; @Override - public void onObbStateChange(String path, String state) { + public void onObbStateChange(String path, int state) { + Log.d(TAG, "Received message. path=" + path + ", state=" + state); synchronized (this) { this.path = path; this.state = state; @@ -76,32 +73,43 @@ public class MountServiceTests extends AndroidTestCase { } } + public String getPath() { + assertTrue("Expected ObbObserver to have received a state change.", done); + return path; + } + + public int getState() { + assertTrue("Expected ObbObserver to have received a state change.", done); + return state; + } + public void reset() { this.path = null; - this.state = null; + this.state = -1; done = false; } public boolean isDone() { return done; } - } - private boolean waitForCompletion(CompletableTask task) { - long waitTime = 0; - synchronized (task) { - while (!task.isDone() && waitTime < MAX_WAIT_TIME) { - try { - task.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } catch (InterruptedException e) { - Log.i(TAG, "Interrupted during sleep", e); + public boolean waitForCompletion() { + long waitTime = 0; + synchronized (this) { + while (!isDone() && waitTime < MAX_WAIT_TIME) { + try { + wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } catch (InterruptedException e) { + Log.i(TAG, "Interrupted during sleep", e); + } } } - } - return task.isDone(); + return isDone(); + } } + private File getFilePath(String name) { final File filesDir = mContext.getFilesDir(); final File outFile = new File(filesDir, name); @@ -128,23 +136,52 @@ public class MountServiceTests extends AndroidTestCase { } private void mountObb(StorageManager sm, final int resource, final File file, - String expectedState) { + int expectedState) { copyRawToFile(resource, file); - ObbObserver observer = new ObbObserver(); + final ObbObserver observer = new ObbObserver(); assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file.getPath(), null, observer)); assertTrue("Mount should have completed", - waitForCompletion(observer)); + observer.waitForCompletion()); + + if (expectedState == OnObbStateChangeListener.MOUNTED) { + assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); + } assertEquals("Actual file and resolved file should be the same", - file.getPath(), observer.path); + file.getPath(), observer.getPath()); + + assertEquals(expectedState, observer.getState()); + } + + private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource, + final File file) { + copyRawToFile(resource, file); + + final ObbObserver observer = new ObbObserver(); + assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file + .getPath(), null, observer)); + + return observer; + } - assertEquals(expectedState, observer.state); + private void waitForObbActionCompletion(final StorageManager sm, final File file, + final ObbObserver observer, int expectedState, boolean checkPath) { + assertTrue("Mount should have completed", observer.waitForCompletion()); + + assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); + + if (checkPath) { + assertEquals("Actual file and resolved file should be the same", file.getPath(), + observer.getPath()); + } + + assertEquals(expectedState, observer.getState()); } - private String checkMountedPath(StorageManager sm, File file) { + private String checkMountedPath(final StorageManager sm, final File file) { final String mountPath = sm.getMountedObbPath(file.getPath()); assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX, OBB_MOUNT_PREFIX, @@ -152,13 +189,21 @@ public class MountServiceTests extends AndroidTestCase { return mountPath; } - private void unmountObb(StorageManager sm, final File outFile) { - ObbObserver observer = new ObbObserver(); + private void unmountObb(final StorageManager sm, final File file, int expectedState) { + final ObbObserver observer = new ObbObserver(); + assertTrue("unmountObb call on test1.obb should succeed", - sm.unmountObb(outFile.getPath(), false, observer)); + sm.unmountObb(file.getPath(), + false, observer)); assertTrue("Unmount should have completed", - waitForCompletion(observer)); + observer.waitForCompletion()); + + assertEquals(expectedState, observer.getState()); + + if (expectedState == OnObbStateChangeListener.UNMOUNTED) { + assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath())); + } } @LargeTest @@ -167,7 +212,9 @@ public class MountServiceTests extends AndroidTestCase { final File outFile = getFilePath("test1.obb"); - mountObb(sm, R.raw.test1, outFile, Environment.MEDIA_MOUNTED); + mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED); + + mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); final String mountPath = checkMountedPath(sm, outFile); final File mountDir = new File(mountPath); @@ -175,7 +222,7 @@ public class MountServiceTests extends AndroidTestCase { assertTrue("OBB mounted path should be a directory", mountDir.isDirectory()); - unmountObb(sm, outFile); + unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED); } @LargeTest @@ -184,7 +231,7 @@ public class MountServiceTests extends AndroidTestCase { final File outFile = getFilePath("test1_nosig.obb"); - mountObb(sm, R.raw.test1_nosig, outFile, Environment.MEDIA_BAD_REMOVAL); + mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL); assertFalse("OBB should not be mounted", sm.isObbMounted(outFile.getPath())); @@ -199,7 +246,8 @@ public class MountServiceTests extends AndroidTestCase { final File outFile = getFilePath("test1_wrongpackage.obb"); - mountObb(sm, R.raw.test1_wrongpackage, outFile, Environment.MEDIA_BAD_REMOVAL); + mountObb(sm, R.raw.test1_wrongpackage, outFile, + OnObbStateChangeListener.ERROR_PERMISSION_DENIED); assertFalse("OBB should not be mounted", sm.isObbMounted(outFile.getPath())); @@ -207,4 +255,31 @@ public class MountServiceTests extends AndroidTestCase { assertNull("OBB's mounted path should be null", sm.getMountedObbPath(outFile.getPath())); } + + @LargeTest + public void testMountAndUnmountTwoObbs() { + StorageManager sm = getStorageManager(); + + final File file1 = getFilePath("test1.obb"); + final File file2 = getFilePath("test2.obb"); + + ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1); + ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2); + + Log.d(TAG, "Waiting for OBB #1 to complete mount"); + waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false); + Log.d(TAG, "Waiting for OBB #2 to complete mount"); + waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false); + + final String mountPath1 = checkMountedPath(sm, file1); + final File mountDir1 = new File(mountPath1); + assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory()); + + final String mountPath2 = checkMountedPath(sm, file2); + final File mountDir2 = new File(mountPath2); + assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory()); + + unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED); + unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED); + } } diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h index 436fc38..51f9aeb 100644 --- a/include/storage/IMountService.h +++ b/include/storage/IMountService.h @@ -61,9 +61,9 @@ public: virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0; virtual void finishMediaUpdate() = 0; virtual void mountObb(const String16& filename, const String16& key, - const sp<IObbActionListener>& token) = 0; + const sp<IObbActionListener>& token, const int32_t nonce) = 0; virtual void unmountObb(const String16& filename, const bool force, - const sp<IObbActionListener>& token) = 0; + const sp<IObbActionListener>& token, const int32_t nonce) = 0; virtual bool isObbMounted(const String16& filename) = 0; virtual bool getMountedObbPath(const String16& filename, String16& path) = 0; }; diff --git a/include/storage/IObbActionListener.h b/include/storage/IObbActionListener.h index 1bedcc6..d78273c 100644 --- a/include/storage/IObbActionListener.h +++ b/include/storage/IObbActionListener.h @@ -29,7 +29,7 @@ class IObbActionListener: public IInterface public: DECLARE_META_INTERFACE(ObbActionListener); - virtual void onObbResult(const String16& filename, const String16& status) = 0; + virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/ui/Input.h b/include/ui/Input.h index 66061fd..8c6018b 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -95,6 +95,10 @@ enum { // Indicates that the input event was injected. POLICY_FLAG_INJECTED = 0x01000000, + // Indicates that the input event is from a trusted source such as a directly attached + // input device or an application with system-wide event injection permission. + POLICY_FLAG_TRUSTED = 0x02000000, + /* These flags are set by the input reader policy as it intercepts each event. */ // Indicates that the screen was off when the event was received and the event diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 0834e86..3599163 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -913,7 +913,6 @@ private: void drainInboundQueueLocked(); void releasePendingEventLocked(); void releaseInboundEventLocked(EventEntry* entry); - bool isEventFromTrustedSourceLocked(EventEntry* entry); // Dispatch state. bool mDispatchEnabled; @@ -960,10 +959,10 @@ private: nsecs_t currentTime, ConfigurationChangedEntry* entry); bool dispatchKeyLocked( nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout, - bool dropEvent, nsecs_t* nextWakeupTime); + DropReason* dropReason, nsecs_t* nextWakeupTime); bool dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, - bool dropEvent, nsecs_t* nextWakeupTime); + DropReason* dropReason, nsecs_t* nextWakeupTime); void dispatchEventToCurrentInputTargetsLocked( nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample); diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp index 3ad9319..9ff6930 100644 --- a/libs/storage/IMountService.cpp +++ b/libs/storage/IMountService.cpp @@ -430,13 +430,14 @@ public: } void mountObb(const String16& filename, const String16& key, - const sp<IObbActionListener>& token) + const sp<IObbActionListener>& token, int32_t nonce) { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); data.writeString16(filename); data.writeString16(key); data.writeStrongBinder(token->asBinder()); + data.writeInt32(nonce); if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) { LOGD("mountObb could not contact remote\n"); return; @@ -448,12 +449,15 @@ public: } } - void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token) + void unmountObb(const String16& filename, const bool force, + const sp<IObbActionListener>& token, const int32_t nonce) { Parcel data, reply; data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); data.writeString16(filename); data.writeInt32(force ? 1 : 0); + data.writeStrongBinder(token->asBinder()); + data.writeInt32(nonce); if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) { LOGD("unmountObb could not contact remote\n"); return; diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp index 5bfece7..eaa211e 100644 --- a/libs/storage/IObbActionListener.cpp +++ b/libs/storage/IObbActionListener.cpp @@ -30,7 +30,7 @@ public: : BpInterface<IObbActionListener>(impl) { } - virtual void onObbResult(const String16& filename, const String16& status) { } + virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { } }; IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener"); @@ -44,8 +44,9 @@ status_t BnObbActionListener::onTransact( case TRANSACTION_onObbResult: { CHECK_INTERFACE(IObbActionListener, data, reply); String16 filename = data.readString16(); - String16 state = data.readString16(); - onObbResult(filename, state); + int32_t nonce = data.readInt32(); + int32_t state = data.readInt32(); + onObbResult(filename, nonce, state); reply->writeNoException(); return NO_ERROR; } break; diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 58c2cdf..41b6ff3 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -370,7 +370,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, } } done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, - dropReason != DROP_REASON_NOT_DROPPED, nextWakeupTime); + &dropReason, nextWakeupTime); break; } @@ -380,7 +380,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, dropReason = DROP_REASON_APP_SWITCH; } done = dispatchMotionLocked(currentTime, typedEntry, - dropReason != DROP_REASON_NOT_DROPPED, nextWakeupTime); + &dropReason, nextWakeupTime); break; } @@ -431,6 +431,9 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR const char* reason; switch (dropReason) { case DROP_REASON_POLICY: +#if DEBUG_INBOUND_EVENT_DETAILS + LOGD("Dropped event because policy requested that it not be delivered to the application."); +#endif reason = "inbound event was dropped because the policy requested that it not be " "delivered to the application"; break; @@ -473,7 +476,7 @@ bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) { bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) { return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) - && isEventFromTrustedSourceLocked(keyEntry) + && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); } @@ -541,12 +544,6 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { mAllocator.releaseEventEntry(entry); } -bool InputDispatcher::isEventFromTrustedSourceLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - return ! injectionState - || hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid); -} - void InputDispatcher::resetKeyRepeatLocked() { if (mKeyRepeatState.lastKeyEntry) { mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); @@ -559,7 +556,8 @@ InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked( KeyEntry* entry = mKeyRepeatState.lastKeyEntry; // Reuse the repeated key entry if it is otherwise unreferenced. - uint32_t policyFlags = entry->policyFlags & (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER); + uint32_t policyFlags = (entry->policyFlags & POLICY_FLAG_RAW_MASK) + | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED; if (entry->refCount == 1) { mAllocator.recycleKeyEntry(entry); entry->eventTime = currentTime; @@ -608,19 +606,13 @@ bool InputDispatcher::dispatchConfigurationChangedLocked( bool InputDispatcher::dispatchKeyLocked( nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout, - bool dropEvent, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t* nextWakeupTime) { // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { - bool trusted; - if (! dropEvent && mFocusedWindow) { - trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState); - } else { - trusted = isEventFromTrustedSourceLocked(entry); - } - if (trusted) { + if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - if (! dropEvent && mFocusedWindow) { + if (mFocusedWindow) { commandEntry->inputChannel = mFocusedWindow->inputChannel; } commandEntry->keyEntry = entry; @@ -630,13 +622,16 @@ bool InputDispatcher::dispatchKeyLocked( entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { + if (*dropReason == DROP_REASON_NOT_DROPPED) { + *dropReason = DROP_REASON_POLICY; + } resetTargetsLocked(); setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_SUCCEEDED); return true; } // Clean up if dropping the event. - if (dropEvent) { + if (*dropReason != DROP_REASON_NOT_DROPPED) { resetTargetsLocked(); setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); return true; @@ -648,7 +643,8 @@ bool InputDispatcher::dispatchKeyLocked( if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN - && ! entry->isInjected()) { + && (entry->policyFlags & POLICY_FLAG_TRUSTED) + && !entry->isInjected()) { if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { // We have seen two identical key downs in a row which indicates that the device @@ -713,9 +709,9 @@ void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyE } bool InputDispatcher::dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, bool dropEvent, nsecs_t* nextWakeupTime) { + nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Clean up if dropping the event. - if (dropEvent) { + if (*dropReason != DROP_REASON_NOT_DROPPED) { resetTargetsLocked(); setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); return true; @@ -2085,6 +2081,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou return; } + policyFlags |= POLICY_FLAG_TRUSTED; mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, keyCode, scanCode, /*byref*/ policyFlags); @@ -2130,6 +2127,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t return; } + policyFlags |= POLICY_FLAG_TRUSTED; mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); bool needWake; @@ -2263,6 +2261,7 @@ void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t swi switchCode, switchValue, policyFlags); #endif + policyFlags |= POLICY_FLAG_TRUSTED; mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags); } @@ -2275,7 +2274,11 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, #endif nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); - bool trusted = hasInjectionPermission(injectorPid, injectorUid); + + uint32_t policyFlags = POLICY_FLAG_INJECTED; + if (hasInjectionPermission(injectorPid, injectorUid)) { + policyFlags |= POLICY_FLAG_TRUSTED; + } EventEntry* injectedEntry; switch (event->getType()) { @@ -2291,11 +2294,8 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t flags = keyEvent->getFlags(); int32_t keyCode = keyEvent->getKeyCode(); int32_t scanCode = keyEvent->getScanCode(); - uint32_t policyFlags = POLICY_FLAG_INJECTED; - if (trusted) { - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); - } + mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, + keyCode, scanCode, /*byref*/ policyFlags); mLock.lock(); injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(), @@ -2314,10 +2314,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } nsecs_t eventTime = motionEvent->getEventTime(); - uint32_t policyFlags = POLICY_FLAG_INJECTED; - if (trusted) { - mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); - } + mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 05dd61b..0f4c1f3 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -28,6 +28,7 @@ #include "ASessionDescription.h" #include <ctype.h> +#include <cutils/properties.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -35,6 +36,9 @@ #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaErrors.h> +#include <arpa/inet.h> +#include <sys/socket.h> + // If no access units are received within 3 secs, assume that the rtp // stream has ended and signal end of stream. static int64_t kAccessUnitTimeoutUs = 3000000ll; @@ -45,6 +49,19 @@ static int64_t kStartupTimeoutUs = 10000000ll; namespace android { +static void MakeUserAgentString(AString *s) { + s->setTo("stagefright/1.1 (Linux;Android "); + +#if (PROPERTY_VALUE_MAX < 8) +#error "PROPERTY_VALUE_MAX must be at least 8" +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.release", value, "Unknown"); + s->append(value); + s->append(")"); +} + static bool GetAttribute(const char *s, const char *key, AString *value) { value->clear(); @@ -137,6 +154,131 @@ struct MyHandler : public AHandler { return maxTimeUs; } + static void addRR(const sp<ABuffer> &buf) { + uint8_t *ptr = buf->data() + buf->size(); + ptr[0] = 0x80 | 0; + ptr[1] = 201; // RR + ptr[2] = 0; + ptr[3] = 1; + ptr[4] = 0xde; // SSRC + ptr[5] = 0xad; + ptr[6] = 0xbe; + ptr[7] = 0xef; + + buf->setRange(0, buf->size() + 8); + } + + static void addSDES(int s, const sp<ABuffer> &buffer) { + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + CHECK_EQ(0, getsockname(s, (sockaddr *)&addr, &addrSize)); + + uint8_t *data = buffer->data() + buffer->size(); + data[0] = 0x80 | 1; + data[1] = 202; // SDES + data[4] = 0xde; // SSRC + data[5] = 0xad; + data[6] = 0xbe; + data[7] = 0xef; + + size_t offset = 8; + + data[offset++] = 1; // CNAME + + AString cname = "stagefright@"; + cname.append(inet_ntoa(addr.sin_addr)); + data[offset++] = cname.size(); + + memcpy(&data[offset], cname.c_str(), cname.size()); + offset += cname.size(); + + data[offset++] = 6; // TOOL + + AString tool; + MakeUserAgentString(&tool); + + data[offset++] = tool.size(); + + memcpy(&data[offset], tool.c_str(), tool.size()); + offset += tool.size(); + + data[offset++] = 0; + + if ((offset % 4) > 0) { + size_t count = 4 - (offset % 4); + switch (count) { + case 3: + data[offset++] = 0; + case 2: + data[offset++] = 0; + case 1: + data[offset++] = 0; + } + } + + size_t numWords = (offset / 4) - 1; + data[2] = numWords >> 8; + data[3] = numWords & 0xff; + + buffer->setRange(buffer->offset(), buffer->size() + offset); + } + + // In case we're behind NAT, fire off two UDP packets to the remote + // rtp/rtcp ports to poke a hole into the firewall for future incoming + // packets. We're going to send an RR/SDES RTCP packet to both of them. + void pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) { + AString source; + AString server_port; + if (!GetAttribute(transport.c_str(), + "source", + &source) + || !GetAttribute(transport.c_str(), + "server_port", + &server_port)) { + return; + } + + int rtpPort, rtcpPort; + if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 + || rtpPort <= 0 || rtpPort > 65535 + || rtcpPort <=0 || rtcpPort > 65535 + || rtcpPort != rtpPort + 1 + || (rtpPort & 1) != 0) { + return; + } + + struct sockaddr_in addr; + memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(source.c_str()); + + if (addr.sin_addr.s_addr == INADDR_NONE) { + return; + } + + // Make up an RR/SDES RTCP packet. + sp<ABuffer> buf = new ABuffer(65536); + buf->setRange(0, 0); + addRR(buf); + addSDES(rtpSocket, buf); + + addr.sin_port = htons(rtpPort); + + ssize_t n = sendto( + rtpSocket, buf->data(), buf->size(), 0, + (const sockaddr *)&addr, sizeof(addr)); + CHECK_EQ(n, (ssize_t)buf->size()); + + addr.sin_port = htons(rtcpPort); + + n = sendto( + rtcpSocket, buf->data(), buf->size(), 0, + (const sockaddr *)&addr, sizeof(addr)); + CHECK_EQ(n, (ssize_t)buf->size()); + + LOGV("successfully poked holes."); + } + virtual void onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case 'conn': @@ -285,6 +427,17 @@ struct MyHandler : public AHandler { sp<AMessage> notify = new AMessage('accu', id()); notify->setSize("track-index", trackIndex); + i = response->mHeaders.indexOfKey("transport"); + CHECK_GE(i, 0); + + if (!track->mUsingInterleavedTCP) { + AString transport = response->mHeaders.valueAt(i); + + pokeAHole(track->mRTPSocket, + track->mRTCPSocket, + transport); + } + mRTPConn->addStream( track->mRTPSocket, track->mRTCPSocket, mSessionDesc, index, diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp index 2f20641..a4233e7 100644 --- a/native/android/storage_manager.cpp +++ b/native/android/storage_manager.cpp @@ -21,10 +21,13 @@ #include <binder/Binder.h> #include <binder/IServiceManager.h> +#include <utils/Atomic.h> #include <utils/Log.h> #include <utils/RefBase.h> #include <utils/String8.h> #include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/threads.h> using namespace android; @@ -38,20 +41,46 @@ public: mStorageManager(mgr) {} - virtual void onObbResult(const android::String16& filename, const android::String16& state); + virtual void onObbResult(const android::String16& filename, const int32_t nonce, + const int32_t state); +}; + +class ObbCallback { +public: + ObbCallback(int32_t _nonce, AStorageManager_obbCallbackFunc _cb, void* _data) + : nonce(_nonce) + , cb(_cb) + , data(_data) + {} + + int32_t nonce; + AStorageManager_obbCallbackFunc cb; + void* data; }; struct AStorageManager : public RefBase { protected: - AStorageManager_obbCallbackFunc mObbCallback; - void* mObbCallbackData; + Mutex mCallbackLock; + Vector<ObbCallback*> mCallbacks; + volatile int32_t mNextNonce; sp<ObbActionListener> mObbActionListener; sp<IMountService> mMountService; + int32_t getNextNonce() { + return android_atomic_inc(&mNextNonce); + } + + ObbCallback* registerObbCallback(AStorageManager_obbCallbackFunc func, void* data) { + ObbCallback* cb = new ObbCallback(getNextNonce(), func, data); + { + AutoMutex _l(mCallbackLock); + mCallbacks.push(cb); + } + return cb; + } + public: AStorageManager() - : mObbCallback(NULL) - , mObbCallbackData(NULL) { } @@ -73,26 +102,40 @@ public: return true; } - void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) { - mObbCallback = cb; - mObbCallbackData = data; - } + void fireCallback(const char* filename, const int32_t nonce, const int32_t state) { + ObbCallback* target = NULL; + { + AutoMutex _l(mCallbackLock); + int N = mCallbacks.size(); + for (int i = 0; i < N; i++) { + ObbCallback* cb = mCallbacks.editItemAt(i); + if (cb->nonce == nonce) { + target = cb; + mCallbacks.removeAt(i); + break; + } + } + } - void fireCallback(const char* filename, const char* state) { - if (mObbCallback != NULL) { - mObbCallback(filename, state, mObbCallbackData); + if (target != NULL) { + target->cb(filename, state, target->data); + delete target; + } else { + LOGI("Didn't find the callback handler for: %s\n", filename); } } - void mountObb(const char* filename, const char* key) { + void mountObb(const char* filename, const char* key, AStorageManager_obbCallbackFunc func, void* data) { + ObbCallback* cb = registerObbCallback(func, data); String16 filename16(filename); String16 key16(key); - mMountService->mountObb(filename16, key16, mObbActionListener); + mMountService->mountObb(filename16, key16, mObbActionListener, cb->nonce); } - void unmountObb(const char* filename, const bool force) { + void unmountObb(const char* filename, const bool force, AStorageManager_obbCallbackFunc func, void* data) { + ObbCallback* cb = registerObbCallback(func, data); String16 filename16(filename); - mMountService->unmountObb(filename16, force, mObbActionListener); + mMountService->unmountObb(filename16, force, mObbActionListener, cb->nonce); } int isObbMounted(const char* filename) { @@ -111,8 +154,8 @@ public: } }; -void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) { - mStorageManager->fireCallback(String8(filename).string(), String8(state).string()); +void ObbActionListener::onObbResult(const android::String16& filename, const int32_t nonce, const int32_t state) { + mStorageManager->fireCallback(String8(filename).string(), nonce, state); } @@ -131,16 +174,14 @@ void AStorageManager_delete(AStorageManager* mgr) { } } -void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) { - mgr->setObbCallback(cb, data); -} - -void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) { - mgr->mountObb(filename, key); +void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key, + AStorageManager_obbCallbackFunc cb, void* data) { + mgr->mountObb(filename, key, cb, data); } -void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force) { - mgr->unmountObb(filename, force != 0); +void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force, + AStorageManager_obbCallbackFunc cb, void* data) { + mgr->unmountObb(filename, force != 0, cb, data); } int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename) { diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h index 6f925c1..c202693 100644 --- a/native/include/android/storage_manager.h +++ b/native/include/android/storage_manager.h @@ -18,6 +18,8 @@ #ifndef ANDROID_STORAGE_MANAGER_H #define ANDROID_STORAGE_MANAGER_H +#include <stdint.h> + #ifdef __cplusplus extern "C" { #endif @@ -25,6 +27,60 @@ extern "C" { struct AStorageManager; typedef struct AStorageManager AStorageManager; +enum { + /* + * The OBB container is now mounted and ready for use. Can be returned + * as the status for callbacks made during asynchronous OBB actions. + */ + AOBB_STATE_MOUNTED = 1, + + /* + * The OBB container is now unmounted and not usable. Can be returned + * as the status for callbacks made during asynchronous OBB actions. + */ + AOBB_STATE_UNMOUNTED = 2, + + /* + * There was an internal system error encountered while trying to + * mount the OBB. Can be returned as the status for callbacks made + * during asynchronous OBB actions. + */ + AOBB_STATE_ERROR_INTERNAL = 20, + + /* + * The OBB could not be mounted by the system. Can be returned as the + * status for callbacks made during asynchronous OBB actions. + */ + AOBB_STATE_ERROR_COULD_NOT_MOUNT = 21, + + /* + * The OBB could not be unmounted. This most likely indicates that a + * file is in use on the OBB. Can be returned as the status for + * callbacks made during asynchronous OBB actions. + */ + AOBB_STATE_ERROR_COULD_NOT_UNMOUNT = 22, + + /* + * A call was made to unmount the OBB when it was not mounted. Can be + * returned as the status for callbacks made during asynchronous OBB + * actions. + */ + AOBB_STATE_ERROR_NOT_MOUNTED = 23, + + /* + * The OBB has already been mounted. Can be returned as the status for + * callbacks made during asynchronous OBB actions. + */ + AOBB_STATE_ERROR_ALREADY_MOUNTED = 24, + + /* + * The current application does not have permission to use this OBB + * because the OBB indicates it's owned by a different package or the + * key used to open it is incorrect. Can be returned as the status for + * callbacks made during asynchronous OBB actions. + */ + AOBB_STATE_ERROR_PERMISSION_DENIED = 25, +}; /** * Obtains a new instance of AStorageManager. @@ -39,22 +95,19 @@ void AStorageManager_delete(AStorageManager* mgr); /** * Callback function for asynchronous calls made on OBB files. */ -typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data); - -/** - * Callback to call when requested asynchronous OBB operation is complete. - */ -void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data); +typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int32_t state, void* data); /** * Attempts to mount an OBB file. This is an asynchronous operation. */ -void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key); +void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key, + AStorageManager_obbCallbackFunc cb, void* data); /** * Attempts to unmount an OBB file. This is an asynchronous operation. */ -void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force); +void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force, + AStorageManager_obbCallbackFunc cb, void* data); /** * Check whether an OBB is mounted. diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 0fccbe7..7995869 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -20,6 +20,10 @@ <activity android:name=".usb.UsbStorageActivity" android:excludeFromRecents="true"> </activity> + <activity android:name="com.android.internal.app.ExternalMediaFormatActivity" + android:theme="@*android:style/Theme.Dialog.Alert" + android:excludeFromRecents="true"> + </activity> </application> </manifest> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java index f3da2a3..b1c6ad8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java @@ -294,6 +294,7 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks mScrollView = (ScrollView)expanded.findViewById(R.id.scroll); mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout); + mExpandedView.setVisibility(View.GONE); mOngoingTitle.setVisibility(View.GONE); mLatestTitle.setVisibility(View.GONE); @@ -335,7 +336,7 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks PixelFormat.RGBX_8888); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("StatusBar"); - // TODO lp.windowAnimations = R.style.Animation_StatusBar; + lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar; WindowManagerImpl.getDefault().addView(view, lp); } @@ -691,6 +692,7 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks mExpandedDialog.getWindow().setAttributes(mExpandedParams); mExpandedView.requestFocus(View.FOCUS_FORWARD); mTrackingView.setVisibility(View.VISIBLE); + mExpandedView.setVisibility(View.VISIBLE); if (!mTicking) { setDateViewVisibility(true, com.android.internal.R.anim.fade_in); @@ -767,6 +769,7 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; mExpandedDialog.getWindow().setAttributes(mExpandedParams); mTrackingView.setVisibility(View.GONE); + mExpandedView.setVisibility(View.GONE); if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); @@ -1323,29 +1326,26 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks mExpandedContents.getLocationInWindow(mPositionTmp); final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); - mExpandedParams.y = pos + mTrackingView.getHeight() - - (mTrackingParams.height-closePos) - contentsBottom; - int max = h; - if (mExpandedParams.y > max) { - mExpandedParams.y = max; - } - int min = mTrackingPosition; - if (mExpandedParams.y < min) { - mExpandedParams.y = min; - } + if (expandedPosition != EXPANDED_LEAVE_ALONE) { + mExpandedParams.y = pos + mTrackingView.getHeight() + - (mTrackingParams.height-closePos) - contentsBottom; + int max = h; + if (mExpandedParams.y > max) { + mExpandedParams.y = max; + } + int min = mTrackingPosition; + if (mExpandedParams.y < min) { + mExpandedParams.y = min; + } - boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; - if (!visible) { - // if the contents aren't visible, move the expanded view way off screen - // because the window itself extends below the content view. - mExpandedParams.y = -disph; - } - mExpandedDialog.getWindow().setAttributes(mExpandedParams); + boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; + if (!visible) { + // if the contents aren't visible, move the expanded view way off screen + // because the window itself extends below the content view. + mExpandedParams.y = -disph; + } + mExpandedDialog.getWindow().setAttributes(mExpandedParams); - // As long as this isn't just a repositioning that's not supposed to affect - // the user's perception of what's showing, call to say that the visibility - // has changed. (Otherwise, someone else will call to do that). - if (expandedPosition != EXPANDED_LEAVE_ALONE) { if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); visibilityChanged(visible); } diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index b9e915a4..47ed7da 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -16,33 +16,17 @@ package com.android.systemui.usb; -import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; import android.content.res.Resources; -import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.storage.IMountService; -import android.os.Message; -import android.os.ServiceManager; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; -import android.os.storage.StorageResultCode; import android.provider.Settings; import android.util.Slog; -import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; public class StorageNotification extends StorageEventListener { private static final String TAG = "StorageNotification"; @@ -165,10 +149,16 @@ public class StorageNotification extends StorageEventListener { * Show safe to unmount media notification, and enable UMS * notification if connected. */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_safe_unmount_notification_title, - com.android.internal.R.string.ext_media_safe_unmount_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard, true, true, null); + if (Environment.isExternalStorageRemovable()) { + setMediaStorageNotification( + com.android.internal.R.string.ext_media_safe_unmount_notification_title, + com.android.internal.R.string.ext_media_safe_unmount_notification_message, + com.android.internal.R.drawable.stat_notify_sdcard, true, true, null); + } else { + // This device does not have removable storage, so + // don't tell the user they can remove it. + setMediaStorageNotification(0, 0, 0, false, false, null); + } updateUsbMassStorageNotification(mUmsAvailable); } } else { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index f21d357..d9bceec 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -1057,6 +1057,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, int keyCode, int metaState, int repeatCount, int policyFlags) { + if ((policyFlags & WindowManagerPolicy.FLAG_TRUSTED) == 0) { + return false; + } + final boolean keyguardOn = keyguardOn(); final boolean down = (action == KeyEvent.ACTION_DOWN); final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0); @@ -1083,7 +1087,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!down) { mHomePressed = false; - if (! canceled) { + if (!canceled) { // If an incoming call is ringing, HOME is totally disabled. // (The user is already on the InCallScreen at this point, // and his ONLY options are to answer or reject the call.) @@ -1735,7 +1739,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags, boolean isScreenOn) { int result = ACTION_PASS_TO_USER; - + if ((policyFlags & WindowManagerPolicy.FLAG_TRUSTED) == 0) { + return result; + } + + if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) { + performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); + } + final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index aa87f29..50b3abe 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -84,7 +84,7 @@ import java.util.Set; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; // How often we perform a backup pass. Privileged external callers can // trigger an immediate pass. diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index ed1243a..df41264 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -359,11 +359,6 @@ public class InputManager { private static final String CALIBRATION_DIR_PATH = "usr/idc/"; @SuppressWarnings("unused") - public void virtualKeyDownFeedback() { - mWindowManagerService.mInputMonitor.virtualKeyDownFeedback(); - } - - @SuppressWarnings("unused") public void notifyConfigurationChanged(long whenNanos) { mWindowManagerService.sendNewConfiguration(); } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 3b2d836..775f5c8 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -44,6 +44,7 @@ import android.os.storage.IMountService; import android.os.storage.IMountServiceListener; import android.os.storage.IMountShutdownObserver; import android.os.storage.IObbActionListener; +import android.os.storage.OnObbStateChangeListener; import android.os.storage.StorageResultCode; import android.security.MessageDigest; import android.util.Slog; @@ -53,7 +54,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -161,25 +161,25 @@ class MountService extends IMountService.Stub final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>(); class ObbState implements IBinder.DeathRecipient { - public ObbState(String filename, IObbActionListener token, int callerUid) + public ObbState(String filename, int callerUid, IObbActionListener token, int nonce) throws RemoteException { this.filename = filename; - this.token = token; this.callerUid = callerUid; - mounted = false; + this.token = token; + this.nonce = nonce; } // OBB source filename - final String filename; - - // Token of remote Binder caller - final IObbActionListener token; + String filename; // Binder.callingUid() final public int callerUid; - // Whether this is mounted currently. - boolean mounted; + // Token of remote Binder caller + final IObbActionListener token; + + // Identifier to pass back to the token + final int nonce; public IBinder getBinder() { return token.asBinder(); @@ -208,8 +208,6 @@ class MountService extends IMountService.Stub sb.append(token.toString()); sb.append(",callerUid="); sb.append(callerUid); - sb.append(",mounted="); - sb.append(mounted); sb.append('}'); return sb.toString(); } @@ -223,6 +221,7 @@ class MountService extends IMountService.Stub private static final int OBB_MCS_BOUND = 2; private static final int OBB_MCS_UNBIND = 3; private static final int OBB_MCS_RECONNECT = 4; + private static final int OBB_FLUSH_MOUNT_STATE = 5; /* * Default Container Service information @@ -500,40 +499,23 @@ class MountService extends IMountService.Stub Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state)); return; } - // Update state on PackageManager + if (Environment.MEDIA_UNMOUNTED.equals(state)) { + // Tell the package manager the media is gone. mPms.updateExternalMediaStatus(false, false); + + /* + * Some OBBs might have been unmounted when this volume was + * unmounted, so send a message to the handler to let it know to + * remove those from the list of mounted OBBS. + */ + mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE, + path)); } else if (Environment.MEDIA_MOUNTED.equals(state)) { + // Tell the package manager the media is available for use. mPms.updateExternalMediaStatus(true, false); } - // Remove all OBB mappings and listeners from this path - synchronized (mObbMounts) { - final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>(); - - final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator(); - while (i.hasNext()) { - final Entry<String, ObbState> obbEntry = i.next(); - - // If this entry's source file is in the volume path that got - // unmounted, remove it because it's no longer valid. - if (obbEntry.getKey().startsWith(path)) { - obbStatesToRemove.add(obbEntry.getValue()); - } - } - - for (final ObbState obbState : obbStatesToRemove) { - removeObbState(obbState); - - try { - obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED); - } catch (RemoteException e) { - Slog.i(TAG, "Couldn't send unmount notification for OBB: " - + obbState.filename); - } - } - } - String oldState = mLegacyState; mLegacyState = state; @@ -1530,6 +1512,10 @@ class MountService extends IMountService.Stub } public String getMountedObbPath(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } + waitForReady(); warnOnNotMounted(); @@ -1552,164 +1538,98 @@ class MountService extends IMountService.Stub } public boolean isObbMounted(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename cannot be null"); + } + synchronized (mObbMounts) { - final ObbState obbState = mObbPathToStateMap.get(filename); - if (obbState != null) { - synchronized (obbState) { - return obbState.mounted; - } - } + return mObbPathToStateMap.containsKey(filename); } - return false; } - public void mountObb(String filename, String key, IObbActionListener token) + public void mountObb(String filename, String key, IObbActionListener token, int nonce) throws RemoteException { - waitForReady(); - warnOnNotMounted(); - if (filename == null) { throw new IllegalArgumentException("filename cannot be null"); - } else if (token == null) { - throw new IllegalArgumentException("token cannot be null"); - } - - final ObbState obbState; - - synchronized (mObbMounts) { - if (isObbMounted(filename)) { - try { - token.onObbResult(filename, Environment.MEDIA_MOUNTED); - } catch (RemoteException e) { - Slog.d(TAG, "Could not send unmount notification for: " + filename); - } - return; - } - - final int callerUid = Binder.getCallingUid(); - obbState = new ObbState(filename, token, callerUid); - addObbState(obbState); } - String hashedKey = null; - if (key != null) { - final MessageDigest md; - try { - md = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - Slog.e(TAG, "Could not load MD5 algorithm", e); - try { - token.onObbResult(filename, Environment.MEDIA_UNMOUNTED); - } catch (RemoteException e1) { - Slog.d(TAG, "Could not send unmount notification for: " + filename); - } - return; - } - - hashedKey = HexDump.toHexString(md.digest(key.getBytes())); + if (token == null) { + throw new IllegalArgumentException("token cannot be null"); } - ObbAction action = new MountObbAction(obbState, hashedKey); + final int callerUid = Binder.getCallingUid(); + final ObbState obbState = new ObbState(filename, callerUid, token, nonce); + final ObbAction action = new MountObbAction(obbState, key); mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); if (DEBUG_OBB) Slog.i(TAG, "Send to OBB handler: " + action.toString()); } - public void unmountObb(String filename, boolean force, IObbActionListener token) { + public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce) + throws RemoteException { if (filename == null) { throw new IllegalArgumentException("filename cannot be null"); - } else if (token == null) { - throw new IllegalArgumentException("token cannot be null"); - } - - final ObbState obbState; - - synchronized (mObbMounts) { - if (!isObbMounted(filename)) { - try { - token.onObbResult(filename, Environment.MEDIA_UNMOUNTED); - } catch (RemoteException e) { - Slog.d(TAG, "Could not send unmount notification for: " + filename); - } - return; - } - - obbState = mObbPathToStateMap.get(filename); - - if (Binder.getCallingUid() != obbState.callerUid) { - throw new SecurityException("caller UID does not match original mount caller UID"); - } else if (!token.asBinder().equals(obbState.getBinder())) { - throw new SecurityException("caller does not match original mount caller"); - } } - ObbAction action = new UnmountObbAction(obbState, force); + final int callerUid = Binder.getCallingUid(); + final ObbState obbState = new ObbState(filename, callerUid, token, nonce); + final ObbAction action = new UnmountObbAction(obbState, force); mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); if (DEBUG_OBB) Slog.i(TAG, "Send to OBB handler: " + action.toString()); } - private void addObbState(ObbState obbState) throws RemoteException { - synchronized (mObbMounts) { - final IBinder binder = obbState.getBinder(); - List<ObbState> obbStates = mObbMounts.get(binder); - final boolean unique; - - if (obbStates == null) { - obbStates = new ArrayList<ObbState>(); - mObbMounts.put(binder, obbStates); - unique = true; - } else { - unique = obbStates.contains(obbState); - } + private void addObbStateLocked(ObbState obbState) throws RemoteException { + final IBinder binder = obbState.getBinder(); + List<ObbState> obbStates = mObbMounts.get(binder); - if (unique) { - obbStates.add(obbState); - try { - obbState.link(); - } catch (RemoteException e) { - /* - * The binder died before we could link it, so clean up our - * state and return failure. - */ - obbStates.remove(obbState); - if (obbStates.isEmpty()) { - mObbMounts.remove(binder); - } - - // Rethrow the error so mountObb can get it - throw e; + if (obbStates == null) { + obbStates = new ArrayList<ObbState>(); + mObbMounts.put(binder, obbStates); + } else { + for (final ObbState o : obbStates) { + if (o.filename.equals(obbState.filename)) { + throw new IllegalStateException("Attempt to add ObbState twice. " + + "This indicates an error in the MountService logic."); } } - - mObbPathToStateMap.put(obbState.filename, obbState); } - } - private void removeObbState(ObbState obbState) { - synchronized (mObbMounts) { - final IBinder binder = obbState.getBinder(); - final List<ObbState> obbStates = mObbMounts.get(binder); - if (obbStates != null) { - if (obbStates.remove(obbState)) { - obbState.unlink(); - } - if (obbStates.isEmpty()) { - mObbMounts.remove(binder); - } + obbStates.add(obbState); + try { + obbState.link(); + } catch (RemoteException e) { + /* + * The binder died before we could link it, so clean up our state + * and return failure. + */ + obbStates.remove(obbState); + if (obbStates.isEmpty()) { + mObbMounts.remove(binder); } - mObbPathToStateMap.remove(obbState.filename); + // Rethrow the error so mountObb can get it + throw e; } + + mObbPathToStateMap.put(obbState.filename, obbState); } - private void replaceObbState(ObbState oldObbState, ObbState newObbState) throws RemoteException { - synchronized (mObbMounts) { - removeObbState(oldObbState); - addObbState(newObbState); + private void removeObbStateLocked(ObbState obbState) { + final IBinder binder = obbState.getBinder(); + final List<ObbState> obbStates = mObbMounts.get(binder); + if (obbStates != null) { + if (obbStates.remove(obbState)) { + obbState.unlink(); + } + if (obbStates.isEmpty()) { + mObbMounts.remove(binder); + } } + + mObbPathToStateMap.remove(obbState.filename); } private class ObbActionHandler extends Handler { @@ -1808,6 +1728,47 @@ class MountService extends IMountService.Stub } break; } + case OBB_FLUSH_MOUNT_STATE: { + final String path = (String) msg.obj; + + if (DEBUG_OBB) + Slog.i(TAG, "Flushing all OBB state for path " + path); + + synchronized (mObbMounts) { + final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>(); + + final Iterator<Entry<String, ObbState>> i = + mObbPathToStateMap.entrySet().iterator(); + while (i.hasNext()) { + final Entry<String, ObbState> obbEntry = i.next(); + + /* + * If this entry's source file is in the volume path + * that got unmounted, remove it because it's no + * longer valid. + */ + if (obbEntry.getKey().startsWith(path)) { + obbStatesToRemove.add(obbEntry.getValue()); + } + } + + for (final ObbState obbState : obbStatesToRemove) { + if (DEBUG_OBB) + Slog.i(TAG, "Removing state for " + obbState.filename); + + removeObbStateLocked(obbState); + + try { + obbState.token.onObbResult(obbState.filename, obbState.nonce, + OnObbStateChangeListener.UNMOUNTED); + } catch (RemoteException e) { + Slog.i(TAG, "Couldn't send unmount notification for OBB: " + + obbState.filename); + } + } + } + break; + } } } @@ -1886,9 +1847,13 @@ class MountService extends IMountService.Stub return obbInfo; } - protected void sendNewStatusOrIgnore(String filename, String status) { + protected void sendNewStatusOrIgnore(int status) { + if (mObbState == null || mObbState.token == null) { + return; + } + try { - mObbState.token.onObbResult(filename, status); + mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status); } catch (RemoteException e) { Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); } @@ -1904,89 +1869,80 @@ class MountService extends IMountService.Stub } public void handleExecute() throws IOException, RemoteException { + waitForReady(); + warnOnNotMounted(); + final ObbInfo obbInfo = getObbInfo(); + if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) { + Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename + + " which is owned by " + obbInfo.packageName); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); + return; + } + + final boolean isMounted; + synchronized (mObbMounts) { + isMounted = mObbPathToStateMap.containsKey(obbInfo.filename); + } + if (isMounted) { + Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); + return; + } + /* - * If someone tried to trick us with some weird characters, rectify - * it here. + * The filename passed in might not be the canonical name, so just + * set the filename to the canonicalized version. */ - if (!mObbState.filename.equals(obbInfo.filename)) { - if (DEBUG_OBB) - Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually " - + obbInfo.filename); - - synchronized (mObbMounts) { - /* - * If the real filename is already mounted, discard this - * state and notify the caller that the OBB is already - * mounted. - */ - if (isObbMounted(obbInfo.filename)) { - if (DEBUG_OBB) - Slog.i(TAG, "OBB already mounted as " + obbInfo.filename); - - removeObbState(mObbState); - sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED); - return; - } + mObbState.filename = obbInfo.filename; - /* - * It's not already mounted, so we have to replace the state - * with the state containing the actual filename. - */ - ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token, - mObbState.callerUid); - replaceObbState(mObbState, newObbState); - mObbState = newObbState; + final String hashedKey; + if (mKey == null) { + hashedKey = "none"; + } else { + final MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Could not load MD5 algorithm", e); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); + return; } - } - if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) { - throw new IllegalArgumentException("Caller package does not match OBB file"); + hashedKey = HexDump.toHexString(md.digest(mKey.getBytes())); } - boolean mounted = false; - int rc; - synchronized (mObbState) { - if (mObbState.mounted) { - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED); - return; + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey, + mObbState.callerUid); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code != VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedInternalError; } + } - rc = StorageResultCode.OperationSucceeded; - String cmd = String.format("obb mount %s %s %d", mObbState.filename, - mKey != null ? mKey : "none", - mObbState.callerUid); - try { - mConnector.doCommand(cmd); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code != VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedInternalError; - } - } + if (rc == StorageResultCode.OperationSucceeded) { + if (DEBUG_OBB) + Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename); - if (rc == StorageResultCode.OperationSucceeded) { - mObbState.mounted = mounted = true; + synchronized (mObbMounts) { + addObbStateLocked(mObbState); } - } - if (mounted) { - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED); + sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED); } else { Slog.e(TAG, "Couldn't mount OBB file: " + rc); - // We didn't succeed, so remove this from the mount-set. - removeObbState(mObbState); - - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT); } } public void handleError() { - removeObbState(mObbState); - - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); } @Override @@ -1999,6 +1955,8 @@ class MountService extends IMountService.Stub sb.append(mObbState.callerUid); sb.append(",token="); sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL"); + sb.append(",binder="); + sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null"); sb.append('}'); return sb.toString(); } @@ -2013,68 +1971,61 @@ class MountService extends IMountService.Stub } public void handleExecute() throws IOException { + waitForReady(); + warnOnNotMounted(); + final ObbInfo obbInfo = getObbInfo(); - /* - * If someone tried to trick us with some weird characters, rectify - * it here. - */ + final ObbState obbState; synchronized (mObbMounts) { - if (!isObbMounted(obbInfo.filename)) { - removeObbState(mObbState); - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED); - return; - } + obbState = mObbPathToStateMap.get(obbInfo.filename); + } - if (!mObbState.filename.equals(obbInfo.filename)) { - removeObbState(mObbState); - mObbState = mObbPathToStateMap.get(obbInfo.filename); - } + if (obbState == null) { + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED); + return; } - boolean unmounted = false; - synchronized (mObbState) { - if (!mObbState.mounted) { - sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED); - return; - } + if (obbState.callerUid != mObbState.callerUid) { + Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename + + " (owned by " + obbInfo.packageName + ")"); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); + return; + } - int rc = StorageResultCode.OperationSucceeded; - String cmd = String.format("obb unmount %s%s", mObbState.filename, - (mForceUnmount ? " force" : "")); - try { - mConnector.doCommand(cmd); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedStorageBusy; - } else if (code == VoldResponseCode.OpFailedStorageNotFound) { - // If it's not mounted then we've already won. - rc = StorageResultCode.OperationSucceeded; - } else { - rc = StorageResultCode.OperationFailedInternalError; - } - } + mObbState.filename = obbInfo.filename; - if (rc == StorageResultCode.OperationSucceeded) { - mObbState.mounted = false; - unmounted = true; + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("obb unmount %s%s", mObbState.filename, + (mForceUnmount ? " force" : "")); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedStorageBusy; + } else if (code == VoldResponseCode.OpFailedStorageNotFound) { + // If it's not mounted then we've already won. + rc = StorageResultCode.OperationSucceeded; + } else { + rc = StorageResultCode.OperationFailedInternalError; } } - if (unmounted) { - removeObbState(mObbState); + if (rc == StorageResultCode.OperationSucceeded) { + synchronized (mObbMounts) { + removeObbStateLocked(obbState); + } - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED); + sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED); } else { - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED); + Slog.w(TAG, "Could not mount OBB: " + mObbState.filename); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT); } } public void handleError() { - removeObbState(mObbState); - - sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL); + sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); } @Override @@ -2090,7 +2041,7 @@ class MountService extends IMountService.Stub sb.append(",token="); sb.append(mObbState.token != null ? mObbState.token.toString() : "null"); sb.append(",binder="); - sb.append(mObbState.getBinder().toString()); + sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null"); sb.append('}'); return sb.toString(); } @@ -2105,16 +2056,27 @@ class MountService extends IMountService.Stub return; } - pw.println(" mObbMounts:"); - synchronized (mObbMounts) { - final Collection<List<ObbState>> obbStateLists = mObbMounts.values(); + pw.println(" mObbMounts:"); - for (final List<ObbState> obbStates : obbStateLists) { + final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator(); + while (binders.hasNext()) { + Entry<IBinder, List<ObbState>> e = binders.next(); + pw.print(" Key="); pw.println(e.getKey().toString()); + final List<ObbState> obbStates = e.getValue(); for (final ObbState obbState : obbStates) { - pw.print(" "); pw.println(obbState.toString()); + pw.print(" "); pw.println(obbState.toString()); } } + + pw.println(""); + pw.println(" mObbPathToStateMap:"); + final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator(); + while (maps.hasNext()) { + final Entry<String, ObbState> e = maps.next(); + pw.print(" "); pw.print(e.getKey()); + pw.print(" -> "); pw.println(e.getValue().toString()); + } } } } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 37b4c1d..bc802a8 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -186,9 +186,11 @@ class PackageManagerService extends IPackageManager.Stub { static final int SCAN_UPDATE_TIME = 1<<6; static final int REMOVE_CHATTY = 1<<16; - + + static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; + static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( - "com.android.defcontainer", + DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService"); private static final String LIB_DIR_NAME = "lib"; @@ -4774,7 +4776,15 @@ class PackageManagerService extends IPackageManager.Stub { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { // Remote call to find out default install location - PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags); + final PackageInfoLite pkgLite; + try { + mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags); + } finally { + mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; @@ -4989,8 +4999,14 @@ class PackageManagerService extends IPackageManager.Stub { libraryPath = new File(dataDir, LIB_DIR_NAME).getPath(); } - boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - return imcs.checkFreeStorage(false, packageURI); + boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + try { + mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + return imcs.checkFreeStorage(false, packageURI); + } finally { + mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } } String getCodePath() { @@ -5034,11 +5050,14 @@ class PackageManagerService extends IPackageManager.Stub { // Copy the resource now int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; try { + mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, + Intent.FLAG_GRANT_READ_URI_PERMISSION); if (imcs.copyResource(packageURI, out)) { ret = PackageManager.INSTALL_SUCCEEDED; } } finally { try { if (out != null) out.close(); } catch (IOException e) {} + mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } return ret; @@ -5209,17 +5228,31 @@ class PackageManagerService extends IPackageManager.Stub { cid = getTempContainerId(); } - boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - return imcs.checkFreeStorage(true, packageURI); + boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { + try { + mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + return imcs.checkFreeStorage(true, packageURI); + } finally { + mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } } int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (temp) { createCopyFile(); } - String newCachePath = imcs.copyResourceToContainer( - packageURI, cid, - getEncryptKey(), RES_FILE_NAME); + + final String newCachePath; + try { + mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + newCachePath = imcs.copyResourceToContainer(packageURI, cid, + getEncryptKey(), RES_FILE_NAME); + } finally { + mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + if (newCachePath != null) { setCachePath(newCachePath); return PackageManager.INSTALL_SUCCEEDED; diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 496c665..88a4c90 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -245,6 +245,9 @@ class PowerManagerService extends IPowerManager.Stub private int[] mButtonBacklightValues; private int[] mKeyboardBacklightValues; private int mLightSensorWarmupTime; + boolean mUnplugTurnsOnScreen; + private int mWarningSpewThrottleCount; + private long mWarningSpewThrottleTime; // Used when logging number and duration of touch-down cycles private long mTotalTouchDownTime; @@ -364,8 +367,12 @@ class PowerManagerService extends IPowerManager.Stub // user activity when screen was already on. // temporarily set mUserActivityAllowed to true so this will work // even when the keyguard is on. + // However, you can also set config_unplugTurnsOnScreen to have it + // turn on. Some devices want this because they don't have a + // charging LED. synchronized (mLocks) { - if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0) { + if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 || + mUnplugTurnsOnScreen) { forceUserActivityLocked(); } } @@ -524,6 +531,9 @@ class PowerManagerService extends IPowerManager.Stub Resources resources = mContext.getResources(); + mUnplugTurnsOnScreen = resources.getBoolean( + com.android.internal.R.bool.config_unplugTurnsOnScreen); + // read settings for auto-brightness mUseSoftwareAutoBrightness = resources.getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); @@ -2095,6 +2105,21 @@ class PowerManagerService extends IPowerManager.Stub return (mScreenBrightness.animating && mScreenBrightness.targetValue == 0); } + private boolean shouldLog(long time) { + synchronized (mLocks) { + if (time > (mWarningSpewThrottleTime + (60*60*1000))) { + mWarningSpewThrottleTime = time; + mWarningSpewThrottleCount = 0; + return true; + } else if (mWarningSpewThrottleCount < 30) { + mWarningSpewThrottleCount++; + return true; + } else { + return false; + } + } + } + private void forceUserActivityLocked() { if (isScreenTurningOffLocked()) { // cancel animation so userActivity will succeed @@ -2112,7 +2137,15 @@ class PowerManagerService extends IPowerManager.Stub } public void userActivity(long time, boolean noChangeLights) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER) + != PackageManager.PERMISSION_GRANTED) { + if (shouldLog(time)) { + Slog.w(TAG, "Caller does not have DEVICE_POWER permission. pid=" + + Binder.getCallingPid() + " uid=" + Binder.getCallingUid()); + } + return; + } + userActivity(time, -1, noChangeLights, OTHER_EVENT, false); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7f42429..26071ae 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -35,7 +35,12 @@ import android.content.pm.IPackageManager; import android.database.ContentObserver; import android.database.Cursor; import android.media.AudioService; -import android.os.*; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.SystemProperties; import android.provider.Contacts.People; import android.provider.Settings; import android.server.BluetoothA2dpService; @@ -573,6 +578,10 @@ public class SystemServer static Timer timer; static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr + // The earliest supported time. We pick one day into 1970, to + // give any timezone code room without going into negative time. + private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000; + /** * This method is called from Zygote to initialize the system. This will cause the native * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back @@ -581,6 +590,16 @@ public class SystemServer native public static void init1(String[] args); public static void main(String[] args) { + if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) { + // If a device's clock is before 1970 (before 0), a lot of + // APIs crash dealing with negative numbers, notably + // java.io.File#setLastModified, so instead we fake it and + // hope that time from cell towers or NTP fixes it + // shortly. + Slog.w(TAG, "System clock is before 1970; setting to 1970."); + SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); + } + if (SamplingProfilerIntegration.isEnabled()) { SamplingProfilerIntegration.start(); timer = new Timer(); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 0de57ab..42fffb0 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5243,13 +5243,6 @@ public class WindowManagerService extends IWindowManager.Stub mTempInputWindows.clear(); } - /* Provides feedback for a virtual key down. */ - public void virtualKeyDownFeedback() { - synchronized (mWindowMap) { - mPolicy.performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); - } - } - /* Notifies that the lid switch changed state. */ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 87271e7..f9c1679 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -493,6 +493,11 @@ public class GpsLocationProvider implements LocationProviderInterface { + " info: " + info); } + if (info != null) { + native_update_network_state(info.isConnected(), info.getType(), + info.isRoaming(), info.getExtraInfo()); + } + if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) { String apnName = info.getExtraInfo(); @@ -1601,4 +1606,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc, int lac, int cid); private native void native_agps_set_id(int type, String setid); + + private native void native_update_network_state(boolean connected, int type, + boolean roaming, String extraInfo); } diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 18037e4..a0b0aba 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -50,7 +50,6 @@ static struct { jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; jmethodID notifyANR; - jmethodID virtualKeyDownFeedback; jmethodID interceptKeyBeforeQueueing; jmethodID interceptKeyBeforeDispatching; jmethodID checkInjectEventsPermission; @@ -192,6 +191,8 @@ public: /* --- InputDispatcherPolicyInterface implementation --- */ + virtual void notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, + uint32_t policyFlags); virtual void notifyConfigurationChanged(nsecs_t when); virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, const sp<InputChannel>& inputChannel); @@ -205,8 +206,6 @@ public: virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags); virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, const KeyEvent* keyEvent, uint32_t policyFlags); - virtual void notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, - uint32_t policyFlags); virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType); virtual bool checkInjectEventsPermissionNonReentrant( int32_t injectorPid, int32_t injectorUid); @@ -255,7 +254,6 @@ private: static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow); - static bool isPolicyKey(int32_t keyCode, bool isScreenOn); static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); static inline JNIEnv* jniEnv() { @@ -291,37 +289,6 @@ void NativeInputManager::dump(String8& dump) { dump.append("\n"); } -bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) { - // Special keys that the WindowManagerPolicy might care about. - switch (keyCode) { - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_ENDCALL: - case AKEYCODE_POWER: - case AKEYCODE_CALL: - case AKEYCODE_HOME: - case AKEYCODE_MENU: - case AKEYCODE_SEARCH: - // media keys - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_FAST_FORWARD: - // The policy always cares about these keys. - return true; - default: - // We need to pass all keys to the policy in the following cases: - // - screen is off - // - keyguard is visible - // - policy is performing key chording - //return ! isScreenOn || keyguardVisible || chording; - return true; // XXX stubbed out for now - } -} - bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { LOGE("An exception was thrown by callback '%s'.", methodName); @@ -454,115 +421,6 @@ bool NativeInputManager::getDisplayInfo(int32_t displayId, return result; } -bool NativeInputManager::isScreenOn() { - return android_server_PowerManagerService_isScreenOn(); -} - -bool NativeInputManager::isScreenBright() { - return android_server_PowerManagerService_isScreenBright(); -} - -void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, - int32_t deviceId, int32_t action, int32_t &flags, - int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("interceptKeyBeforeQueueing - when=%lld, deviceId=%d, action=%d, flags=%d, " - "keyCode=%d, scanCode=%d, policyFlags=0x%x", - when, deviceId, action, flags, keyCode, scanCode, policyFlags); -#endif - - bool down = action == AKEY_EVENT_ACTION_DOWN; - if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { - policyFlags |= POLICY_FLAG_VIRTUAL; - flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; - - if (down) { - JNIEnv* env = jniEnv(); - env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback); - checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback"); - } - } - - const int32_t WM_ACTION_PASS_TO_USER = 1; - const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2; - const int32_t WM_ACTION_GO_TO_SLEEP = 4; - - bool isScreenOn = this->isScreenOn(); - bool isScreenBright = this->isScreenBright(); - - jint wmActions = 0; - if (isPolicyKey(keyCode, isScreenOn)) { - JNIEnv* env = jniEnv(); - - wmActions = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.interceptKeyBeforeQueueing, - when, keyCode, down, policyFlags, isScreenOn); - if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { - wmActions = 0; - } - } else { - wmActions = WM_ACTION_PASS_TO_USER; - } - - if (! isScreenOn) { - // Key presses and releases wake the device. - policyFlags |= POLICY_FLAG_WOKE_HERE; - flags |= AKEY_EVENT_FLAG_WOKE_HERE; - } - - if (! isScreenBright) { - // Key presses and releases brighten the screen if dimmed. - policyFlags |= POLICY_FLAG_BRIGHT_HERE; - } - - if (wmActions & WM_ACTION_GO_TO_SLEEP) { - android_server_PowerManagerService_goToSleep(when); - } - - if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { - android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT); - } - - if (wmActions & WM_ACTION_PASS_TO_USER) { - policyFlags |= POLICY_FLAG_PASS_TO_USER; - } -} - -void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags); -#endif - - if (isScreenOn()) { - // Only dispatch events when the device is awake. - // Do not wake the device. - policyFlags |= POLICY_FLAG_PASS_TO_USER; - - if (! isScreenBright()) { - // Brighten the screen if dimmed. - policyFlags |= POLICY_FLAG_BRIGHT_HERE; - } - } -} - -void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode, - int32_t switchValue, uint32_t policyFlags) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("notifySwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x", - when, switchCode, switchValue, policyFlags); -#endif - - JNIEnv* env = jniEnv(); - - switch (switchCode) { - case SW_LID: - env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, - when, switchValue == 0); - checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged"); - break; - } -} - bool NativeInputManager::filterTouchEvents() { if (mFilterTouchEvents < 0) { JNIEnv* env = jniEnv(); @@ -692,6 +550,24 @@ void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDevi } } +void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode, + int32_t switchValue, uint32_t policyFlags) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("notifySwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x", + when, switchCode, switchValue, policyFlags); +#endif + + JNIEnv* env = jniEnv(); + + switch (switchCode) { + case SW_LID: + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, + when, switchValue == 0); + checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged"); + break; + } +} + void NativeInputManager::notifyConfigurationChanged(nsecs_t when) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyConfigurationChanged - when=%lld", when); @@ -944,13 +820,88 @@ void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen); } -bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, - const KeyEvent* keyEvent, uint32_t policyFlags) { +bool NativeInputManager::isScreenOn() { + return android_server_PowerManagerService_isScreenOn(); +} + +bool NativeInputManager::isScreenBright() { + return android_server_PowerManagerService_isScreenBright(); +} + +void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, + int32_t deviceId, int32_t action, int32_t &flags, + int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("interceptKeyBeforeQueueing - when=%lld, deviceId=%d, action=%d, flags=%d, " + "keyCode=%d, scanCode=%d, policyFlags=0x%x", + when, deviceId, action, flags, keyCode, scanCode, policyFlags); +#endif + + if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { + policyFlags |= POLICY_FLAG_VIRTUAL; + flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; + } + + const int32_t WM_ACTION_PASS_TO_USER = 1; + const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2; + const int32_t WM_ACTION_GO_TO_SLEEP = 4; + bool isScreenOn = this->isScreenOn(); - if (! isPolicyKey(keyEvent->getKeyCode(), isScreenOn)) { - return false; + bool isScreenBright = this->isScreenBright(); + + JNIEnv* env = jniEnv(); + jint wmActions = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeQueueing, + when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn); + if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { + wmActions = 0; + } + + if (policyFlags & POLICY_FLAG_TRUSTED) { + if (! isScreenOn) { + // Key presses and releases wake the device. + policyFlags |= POLICY_FLAG_WOKE_HERE; + flags |= AKEY_EVENT_FLAG_WOKE_HERE; + } + + if (! isScreenBright) { + // Key presses and releases brighten the screen if dimmed. + policyFlags |= POLICY_FLAG_BRIGHT_HERE; + } + + if (wmActions & WM_ACTION_GO_TO_SLEEP) { + android_server_PowerManagerService_goToSleep(when); + } + + if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { + android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT); + } } + if (wmActions & WM_ACTION_PASS_TO_USER) { + policyFlags |= POLICY_FLAG_PASS_TO_USER; + } +} + +void NativeInputManager::interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("interceptGenericBeforeQueueing - when=%lld, policyFlags=0x%x", when, policyFlags); +#endif + + if (isScreenOn()) { + // Only dispatch events when the device is awake. + // Do not wake the device. + policyFlags |= POLICY_FLAG_PASS_TO_USER; + + if ((policyFlags & POLICY_FLAG_TRUSTED) && !isScreenBright()) { + // Brighten the screen if dimmed. + policyFlags |= POLICY_FLAG_BRIGHT_HERE; + } + } +} + +bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, + const KeyEvent* keyEvent, uint32_t policyFlags) { JNIEnv* env = jniEnv(); // Note: inputChannel may be null. @@ -1365,9 +1316,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J"); - GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz, - "virtualKeyDownFeedback", "()V"); - GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, "interceptKeyBeforeQueueing", "(JIZIZ)I"); diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp index bd722d7..43e8467 100755 --- a/services/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp @@ -586,6 +586,23 @@ static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* e return result; } +static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject obj, + jboolean connected, int type, jboolean roaming, jstring extraInfo) +{ + const AGpsRilInterface* interface = GetAGpsRilInterface(env, obj); + if (interface && + (interface->size > ((char *)&interface->update_network_state - (char *)&interface)) && + interface->update_network_state) { + if (extraInfo) { + const char *extraInfoStr = env->GetStringUTFChars(extraInfo, NULL); + interface->update_network_state(connected, type, roaming, extraInfoStr); + env->ReleaseStringUTFChars(extraInfo, extraInfoStr); + } else { + interface->update_network_state(connected, type, roaming, NULL); + } + } +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, @@ -611,6 +628,7 @@ static JNINativeMethod sMethods[] = { {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, {"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message}, {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state}, + {"native_update_network_state", "(ZIZLjava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state }, }; int register_android_server_location_GpsLocationProvider(JNIEnv* env) diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 758b408..069b85a 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -444,11 +444,11 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } if (transform & HAL_TRANSFORM_FLIP_V) { swap(vLT, vLB); - swap(vRB, vRT); + swap(vRT, vRB); } if (transform & HAL_TRANSFORM_FLIP_H) { - swap(vLT, vRB); - swap(vLB, vRT); + swap(vLT, vRT); + swap(vLB, vRB); } TexCoords texCoords[4]; diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp index d668e88..aebe1b8 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/MessageQueue.cpp @@ -72,14 +72,6 @@ sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout) nsecs_t now = systemTime(); nsecs_t nextEventTime = -1; - // invalidate messages are always handled first - if (mInvalidate) { - mInvalidate = false; - mInvalidateMessage->when = now; - result = mInvalidateMessage; - break; - } - LIST::iterator cur(mMessages.begin()); if (cur != mMessages.end()) { result = *cur; @@ -91,17 +83,29 @@ sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout) mMessages.remove(cur); break; } - if (timeout>=0 && timeoutTime < now) { - // we timed-out, return a NULL message - result = 0; - break; - } nextEventTime = result->when; result = 0; } - if (timeout >= 0 && nextEventTime > 0) { - if (nextEventTime > timeoutTime) { + // see if we have an invalidate message + if (mInvalidate) { + mInvalidate = false; + mInvalidateMessage->when = now; + result = mInvalidateMessage; + break; + } + + if (timeout >= 0) { + if (timeoutTime < now) { + // we timed-out, return a NULL message + result = 0; + break; + } + if (nextEventTime > 0) { + if (nextEventTime > timeoutTime) { + nextEventTime = timeoutTime; + } + } else { nextEventTime = timeoutTime; } } diff --git a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java index 9b6a850..99f0abe 100644 --- a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java +++ b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java @@ -134,17 +134,15 @@ public class MockRilController { return curstate == state; } - - /** - * Set an MT call + * Start an incoming call for the given phone number * - * @param phoneNumber for the number shown + * @param phoneNumber is the number to show as incoming call + * @return true if the incoming call is started successfully, false if it fails. */ - public boolean setMTCall(String phoneNumber) { + public boolean startIncomingCall(String phoneNumber) { RilCtrlCmds.CtrlReqSetMTCall req = new RilCtrlCmds.CtrlReqSetMTCall(); - // Check whether it is a valid number req.setPhoneNumber(phoneNumber); if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, req)) { Log.v(TAG, "send CMD_SET_MT_CALL request failed"); @@ -153,4 +151,68 @@ public class MockRilController { return true; } + /** + * Hang up a connection remotelly for the given call fail cause + * + * @param connectionID is the connection to be hung up + * @param failCause is the call fail cause defined in ril.h + * @return true if the hangup is successful, false if it fails + */ + public boolean hangupRemote(int connectionId, int failCause) { + RilCtrlCmds.CtrlHangupConnRemote req = new RilCtrlCmds.CtrlHangupConnRemote(); + req.setConnectionId(connectionId); + req.setCallFailCause(failCause); + + if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, req)) { + Log.v(TAG, "send CTRL_CMD_HANGUP_CONN_REMOTE request failed"); + return false; + } + return true; + } + + /** + * Set call transition flag to the Mock Ril + * + * @param flag is a boolean value for the call transiton flag + * true: call transition: dialing->alert, alert->active is controlled + * false: call transition is automatically handled by Mock Ril + * @return true if the request is successful, false if it failed to set the flag + */ + public boolean setCallTransitionFlag(boolean flag) { + RilCtrlCmds.CtrlSetCallTransitionFlag req = new RilCtrlCmds.CtrlSetCallTransitionFlag(); + + req.setFlag(flag); + + if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, req)) { + Log.v(TAG, "send CTRL_CMD_SET_CALL_TRANSITION_FLAG request failed"); + return false; + } + return true; + } + + /** + * Set the dialing call to alert if the call transition flag is true + * + * @return true if the call transition is successful, false if it fails + */ + public boolean setDialCallToAlert() { + if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null)) { + Log.v(TAG, "send CTRL_CMD_SET_CALL_ALERT request failed"); + return false; + } + return true; + } + + /** + * Set the alert call to active if the call transition flag is true + * + * @return true if the call transition is successful, false if it fails + */ + public boolean setAlertCallToActive() { + if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null)) { + Log.v(TAG, "send CTRL_CMD_SET_CALL_ACTIVE request failed"); + return false; + } + return true; + } } diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java index f0d5b31..3149ee1 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java @@ -55,7 +55,7 @@ public class MockRilTest extends InstrumentationTestCase { } /** - * Test protobuf serialization and deserialization + * Test Case 1: Test protobuf serialization and deserialization * @throws InvalidProtocolBufferMicroException */ public void testProtobufSerDes() throws InvalidProtocolBufferMicroException { @@ -77,7 +77,7 @@ public class MockRilTest extends InstrumentationTestCase { } /** - * Test echo command works using writeMsg & readMsg + * Test case 2: Test echo command works using writeMsg & readMsg */ public void testEchoMsg() throws IOException { log("testEchoMsg E"); @@ -110,7 +110,7 @@ public class MockRilTest extends InstrumentationTestCase { } /** - * Test get as + * Test case 3: Test get as */ public void testGetAs() { log("testGetAs E"); @@ -150,6 +150,9 @@ public class MockRilTest extends InstrumentationTestCase { log("testGetAs X"); } + /** + * Test case 3: test get radio state + */ public void testGetRadioState() throws IOException { log("testGetRadioState E"); @@ -175,6 +178,9 @@ public class MockRilTest extends InstrumentationTestCase { log("testGetRadioState X"); } + /** + * Test case 5: test set radio state + */ public void testSetRadioState() throws IOException { log("testSetRadioState E"); @@ -187,6 +193,9 @@ public class MockRilTest extends InstrumentationTestCase { Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_RADIO_STATE, 0, 0, cmdrs); Msg resp = Msg.recv(mMockRilChannel); + log("get response status :" + resp.getStatus()); + log("get response for command: " + resp.getCmd()); + log("get command token: " + resp.getToken()); RilCtrlCmds.CtrlRspRadioState rsp = resp.getDataAs(RilCtrlCmds.CtrlRspRadioState.class); @@ -194,4 +203,102 @@ public class MockRilTest extends InstrumentationTestCase { log("get response for testSetRadioState: " + state); assertTrue(RilCmds.RADIOSTATE_SIM_NOT_READY == state); } + + /** + * Test case 6: test start incoming call and hangup it. + */ + public void testStartIncomingCallAndHangup() throws IOException { + log("testStartIncomingCallAndHangup"); + RilCtrlCmds.CtrlReqSetMTCall cmd = new RilCtrlCmds.CtrlReqSetMTCall(); + String incomingCall = "6502889108"; + // set the MT call + cmd.setPhoneNumber(incomingCall); + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, cmd); + // get response + Msg resp = Msg.recv(mMockRilChannel); + log("Get response status: " + resp.getStatus()); + assertTrue("The ril is not in a proper state to set MT calls.", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + + // allow the incoming call alerting for some time + try { + Thread.sleep(5000); + } catch (InterruptedException e) {} + + // we are playing a trick to assume the current is 1 + RilCtrlCmds.CtrlHangupConnRemote hangupCmd = new RilCtrlCmds.CtrlHangupConnRemote(); + hangupCmd.setConnectionId(1); + hangupCmd.setCallFailCause(16); // normal hangup + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, hangupCmd); + + // get response + resp = Msg.recv(mMockRilChannel); + log("Get response for hangup connection: " + resp.getStatus()); + assertTrue("CTRL_CMD_HANGUP_CONN_REMOTE failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + } + + /** + * Test case 7: test set call transition flag + */ + public void testSetCallTransitionFlag() throws IOException { + log("testSetCallTransitionFlag"); + // Set flag to true: + RilCtrlCmds.CtrlSetCallTransitionFlag cmd = new RilCtrlCmds.CtrlSetCallTransitionFlag(); + cmd.setFlag(true); + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, cmd); + + Msg resp = Msg.recv(mMockRilChannel); + log("Get response status: " + resp.getStatus()); + assertTrue("Set call transition flag failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + + // add a dialing call + RilCtrlCmds.CtrlReqAddDialingCall cmdDialCall = new RilCtrlCmds.CtrlReqAddDialingCall(); + String phoneNumber = "5102345678"; + cmdDialCall.setPhoneNumber(phoneNumber); + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_ADD_DIALING_CALL, 0, 0, cmdDialCall); + resp = Msg.recv(mMockRilChannel); + log("Get response status for adding a dialing call: " + resp.getStatus()); + assertTrue("add dialing call failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + try { + Thread.sleep(5000); + } catch (InterruptedException e) {} + + // send command to force call state change + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null); + resp = Msg.recv(mMockRilChannel); + log("Get response status: " + resp.getStatus()); + assertTrue("Set call alert failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + + try { + Thread.sleep(2000); + } catch (InterruptedException e) {} + + // send command to force call state change + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null); + resp = Msg.recv(mMockRilChannel); + log("Get response status: " + resp.getStatus()); + assertTrue("Set call active failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + + // hangup the active all remotely + RilCtrlCmds.CtrlHangupConnRemote hangupCmd = new RilCtrlCmds.CtrlHangupConnRemote(); + hangupCmd.setConnectionId(1); + hangupCmd.setCallFailCause(16); // normal hangup + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, hangupCmd); + resp = Msg.recv(mMockRilChannel); + log("Get response for hangup connection: " + resp.getStatus()); + assertTrue("CTRL_CMD_HANGUP_CONN_REMOTE failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + + // set the flag to false + cmd.setFlag(false); + Msg.send(mMockRilChannel, RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, cmd); + resp = Msg.recv(mMockRilChannel); + assertTrue("Set call transition flag failed", + resp.getStatus() == RilCtrlCmds.CTRL_STATUS_OK); + } } |
