diff options
33 files changed, 2028 insertions, 1568 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 1fe85e6..9a55a6f 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; import android.os.ServiceManager; +import android.os.StrictMode; import android.text.TextUtils; import android.util.Config; import android.util.Log; @@ -1056,8 +1057,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder app = data.readStrongBinder(); int violationMask = data.readInt(); - ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); - handleApplicationStrictModeViolation(app, violationMask, ci); + StrictMode.ViolationInfo info = new StrictMode.ViolationInfo(data); + handleApplicationStrictModeViolation(app, violationMask, info); reply.writeNoException(); return true; } @@ -2571,14 +2572,14 @@ class ActivityManagerProxy implements IActivityManager public void handleApplicationStrictModeViolation(IBinder app, int violationMask, - ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException + StrictMode.ViolationInfo info) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(app); data.writeInt(violationMask); - crashInfo.writeToParcel(data, 0); + info.writeToParcel(data, 0); mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0); reply.readException(); reply.recycle(); diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index 30815c3..8f940d5 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; import android.util.Printer; @@ -74,18 +75,15 @@ public class ApplicationErrorReport implements Parcelable { public static final int TYPE_BATTERY = 3; /** - * An error report about a StrictMode violation. - */ - public static final int TYPE_STRICT_MODE_VIOLATION = 4; - - /** - * An error report about a StrictMode violation. + * A report from a user to a developer about a running service that the + * user doesn't think should be running. */ public static final int TYPE_RUNNING_SERVICE = 5; /** * Type of this report. Can be one of {@link #TYPE_NONE}, - * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}. + * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, {@link #TYPE_BATTERY}, + * or {@link #TYPE_RUNNING_SERVICE}. */ public int type; @@ -133,7 +131,7 @@ public class ApplicationErrorReport implements Parcelable { * of BatteryInfo; otherwise null. */ public BatteryInfo batteryInfo; - + /** * If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance * of RunningServiceInfo; otherwise null. @@ -278,10 +276,6 @@ public class ApplicationErrorReport implements Parcelable { /** * Describes an application crash. - * - * <p>This is also used to marshal around stack traces of ANRs and - * StrictMode violations which aren't necessarily crashes, but have - * a lot in common. */ public static class CrashInfo { /** @@ -320,12 +314,6 @@ public class ApplicationErrorReport implements Parcelable { public String stackTrace; /** - * For StrictMode violations, the wall time duration of the - * violation, when known. - */ - public long durationMillis = -1; - - /** * Create an uninitialized instance of CrashInfo. */ public CrashInfo() { @@ -368,7 +356,6 @@ public class ApplicationErrorReport implements Parcelable { throwMethodName = in.readString(); throwLineNumber = in.readInt(); stackTrace = in.readString(); - durationMillis = in.readLong(); } /** @@ -382,7 +369,6 @@ public class ApplicationErrorReport implements Parcelable { dest.writeString(throwMethodName); dest.writeInt(throwLineNumber); dest.writeString(stackTrace); - dest.writeLong(durationMillis); } /** @@ -396,9 +382,6 @@ public class ApplicationErrorReport implements Parcelable { pw.println(prefix + "throwMethodName: " + throwMethodName); pw.println(prefix + "throwLineNumber: " + throwLineNumber); pw.println(prefix + "stackTrace: " + stackTrace); - if (durationMillis != -1) { - pw.println(prefix + "durationMillis: " + durationMillis); - } } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 20c9a80..81b28b9 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -19,10 +19,10 @@ package android.app; import android.content.ComponentName; import android.content.ContentProviderNative; import android.content.IContentProvider; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; -import android.content.IIntentSender; -import android.content.IIntentReceiver; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -31,14 +31,15 @@ import android.content.pm.ProviderInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; +import android.os.Bundle; import android.os.Debug; -import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelFileDescriptor; -import android.os.Bundle; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.StrictMode; import java.util.List; @@ -260,7 +261,7 @@ public interface IActivityManager extends IInterface { // bit violated and penalty bits to be executed by the // ActivityManagerService remaining set. public void handleApplicationStrictModeViolation(IBinder app, int violationMask, - ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; + StrictMode.ViolationInfo crashInfo) throws RemoteException; /* * This will deliver the specified signal to all the persistent processes. Currently only diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index b7a750b..00063af 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -403,7 +403,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * * <p class="caution">Note that the system calls this on your * service's main thread. A service's main thread is the same - * thread where UI operations place for Activities running in the + * thread where UI operations take place for Activities running in the * same process. You should always avoid stalling the main * thread's event loop. When doing long-running operations, * network calls, or heavy disk I/O, you should kick off a new diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 73c4011..0b35d8b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -587,7 +587,7 @@ public class PackageParser { * location from the apk location at the given file path. * @param packageFilePath file location of the apk * @param flags Special parse flags - * @return PackageLite object with package information. + * @return PackageLite object with package information or null on failure. */ public static PackageLite parsePackageLite(String packageFilePath, int flags) { XmlResourceParser parser = null; diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index d4b0500..ac12e10 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -18,6 +18,7 @@ package android.os; import android.app.ActivityManagerNative; import android.app.ApplicationErrorReport; import android.util.Log; +import android.util.Printer; import com.android.internal.os.RuntimeInit; @@ -97,9 +98,9 @@ public final class StrictMode { * via Parcel.writeNoException() (amusingly) where the caller can * choose how to react. */ - private static final ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations = - new ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>>() { - @Override protected ArrayList<ApplicationErrorReport.CrashInfo> initialValue() { + private static final ThreadLocal<ArrayList<ViolationInfo>> gatheredViolations = + new ThreadLocal<ArrayList<ViolationInfo>>() { + @Override protected ArrayList<ViolationInfo> initialValue() { // Starts null to avoid unnecessary allocations when // checking whether there are any violations or not in // hasGatheredViolations() below. @@ -240,7 +241,9 @@ public final class StrictMode { if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) { return; } - startHandlingViolationException(new StrictModeDiskWriteViolation(mPolicyMask)); + BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask); + e.fillInStackTrace(); + startHandlingViolationException(e); } // Part of BlockGuard.Policy interface: @@ -248,7 +251,9 @@ public final class StrictMode { if ((mPolicyMask & DISALLOW_DISK_READ) == 0) { return; } - startHandlingViolationException(new StrictModeDiskReadViolation(mPolicyMask)); + BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask); + e.fillInStackTrace(); + startHandlingViolationException(e); } // Part of BlockGuard.Policy interface: @@ -256,7 +261,9 @@ public final class StrictMode { if ((mPolicyMask & DISALLOW_NETWORK) == 0) { return; } - startHandlingViolationException(new StrictModeNetworkViolation(mPolicyMask)); + BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask); + e.fillInStackTrace(); + startHandlingViolationException(e); } public void setPolicyMask(int policyMask) { @@ -269,31 +276,70 @@ public final class StrictMode { // thread and, if so, uses it to roughly measure how long the // violation took. void startHandlingViolationException(BlockGuard.BlockGuardPolicyException e) { - e.fillInStackTrace(); - final ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(e); - crashInfo.durationMillis = -1; // unknown - final int savedPolicy = mPolicyMask; + final ViolationInfo info = new ViolationInfo(e, e.getPolicy()); + info.violationUptimeMillis = SystemClock.uptimeMillis(); + 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 + // until the time the looper is idle again (right before + // the next epoll_wait) + void handleViolationWithTimingAttempt(final ViolationInfo info) { Looper looper = Looper.myLooper(); + + // Without a Looper, we're unable to time how long the + // violation takes place. This case should be rare, as + // most users will care about timing violations that + // happen on their main UI thread. Note that this case is + // also hit when a violation takes place in a Binder + // thread, in "gather" mode. In this case, the duration + // of the violation is computed by the ultimate caller and + // its Looper, if any. + // TODO: if in gather mode, ignore Looper.myLooper() and always + // go into this immediate mode? if (looper == null) { - // Without a Looper, we're unable to time how long the - // violation takes place. This case should be rare, - // as most users will care about timing violations - // that happen on their main UI thread. - handleViolation(crashInfo, savedPolicy); - } else { - MessageQueue queue = Looper.myQueue(); - final long violationTime = SystemClock.uptimeMillis(); - queue.addIdleHandler(new MessageQueue.IdleHandler() { - public boolean queueIdle() { - long afterViolationTime = SystemClock.uptimeMillis(); - crashInfo.durationMillis = afterViolationTime - violationTime; - handleViolation(crashInfo, savedPolicy); - return false; // remove this idle handler from the array - } - }); + info.durationMillis = -1; // unknown (redundant, already set) + handleViolation(info); + return; } + MessageQueue queue = Looper.myQueue(); + final ArrayList<ViolationInfo> records = violationsBeingTimed.get(); + if (records.size() >= 10) { + // Not worth measuring. Too many offenses in one loop. + return; + } + records.add(info); + if (records.size() > 1) { + // There's already been a violation this loop, so we've already + // registered an idle handler to process the list of violations + // at the end of this Looper's loop. + return; + } + + queue.addIdleHandler(new MessageQueue.IdleHandler() { + public boolean queueIdle() { + long loopFinishTime = SystemClock.uptimeMillis(); + for (int n = 0; n < records.size(); ++n) { + ViolationInfo v = records.get(n); + v.violationNumThisLoop = n + 1; + v.durationMillis = + (int) (loopFinishTime - v.violationUptimeMillis); + handleViolation(v); + } + records.clear(); + return false; // remove this idle handler from the array + } + }); } // Note: It's possible (even quite likely) that the @@ -301,37 +347,35 @@ public final class StrictMode { // violation fired and now (after the violating code ran) due // to people who push/pop temporary policy in regions of code, // hence the policy being passed around. - void handleViolation( - final ApplicationErrorReport.CrashInfo crashInfo, - int policy) { - if (crashInfo.stackTrace == null) { - Log.d(TAG, "unexpected null stacktrace"); + void handleViolation(final ViolationInfo info) { + if (info == null || info.crashInfo == null || info.crashInfo.stackTrace == null) { + Log.wtf(TAG, "unexpected null stacktrace"); return; } - if (LOG_V) Log.d(TAG, "handleViolation; policy=" + policy); + if (LOG_V) Log.d(TAG, "handleViolation; policy=" + info.policy); - if ((policy & PENALTY_GATHER) != 0) { - ArrayList<ApplicationErrorReport.CrashInfo> violations = gatheredViolations.get(); + if ((info.policy & PENALTY_GATHER) != 0) { + ArrayList<ViolationInfo> violations = gatheredViolations.get(); if (violations == null) { - violations = new ArrayList<ApplicationErrorReport.CrashInfo>(1); + violations = new ArrayList<ViolationInfo>(1); gatheredViolations.set(violations); } else if (violations.size() >= 5) { // Too many. In a loop or something? Don't gather them all. return; } - for (ApplicationErrorReport.CrashInfo previous : violations) { - if (crashInfo.stackTrace.equals(previous.stackTrace)) { + for (ViolationInfo previous : violations) { + if (info.crashInfo.stackTrace.equals(previous.crashInfo.stackTrace)) { // Duplicate. Don't log. return; } } - violations.add(crashInfo); + violations.add(info); return; } // Not perfect, but fast and good enough for dup suppression. - Integer crashFingerprint = crashInfo.stackTrace.hashCode(); + Integer crashFingerprint = info.crashInfo.stackTrace.hashCode(); long lastViolationTime = 0; if (mLastViolationTime.containsKey(crashFingerprint)) { lastViolationTime = mLastViolationTime.get(crashFingerprint); @@ -341,13 +385,13 @@ public final class StrictMode { long timeSinceLastViolationMillis = lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime); - if ((policy & PENALTY_LOG) != 0 && + if ((info.policy & PENALTY_LOG) != 0 && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { - if (crashInfo.durationMillis != -1) { + if (info.durationMillis != -1) { Log.d(TAG, "StrictMode policy violation; ~duration=" + - crashInfo.durationMillis + " ms: " + crashInfo.stackTrace); + info.durationMillis + " ms: " + info.crashInfo.stackTrace); } else { - Log.d(TAG, "StrictMode policy violation: " + crashInfo.stackTrace); + Log.d(TAG, "StrictMode policy violation: " + info.crashInfo.stackTrace); } } @@ -355,20 +399,20 @@ public final class StrictMode { // subset of the original StrictMode policy bitmask, with // only the bit violated and penalty bits to be executed // by the ActivityManagerService remaining set. - int violationMask = 0; + int violationMaskSubset = 0; - if ((policy & PENALTY_DIALOG) != 0 && + if ((info.policy & PENALTY_DIALOG) != 0 && timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) { - violationMask |= PENALTY_DIALOG; + violationMaskSubset |= PENALTY_DIALOG; } - if ((policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) { - violationMask |= PENALTY_DROPBOX; + if ((info.policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) { + violationMaskSubset |= PENALTY_DROPBOX; } - if (violationMask != 0) { - int violationBit = parseViolationFromMessage(crashInfo.exceptionMessage); - violationMask |= violationBit; + if (violationMaskSubset != 0) { + int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage); + violationMaskSubset |= violationBit; final int savedPolicy = getThreadBlockingPolicy(); try { // First, remove any policy before we call into the Activity Manager, @@ -379,8 +423,8 @@ public final class StrictMode { ActivityManagerNative.getDefault().handleApplicationStrictModeViolation( RuntimeInit.getApplicationObject(), - violationMask, - crashInfo); + violationMaskSubset, + info); } catch (RemoteException e) { Log.e(TAG, "RemoteException trying to handle StrictMode violation", e); } finally { @@ -389,7 +433,7 @@ public final class StrictMode { } } - if ((policy & PENALTY_DEATH) != 0) { + if ((info.policy & PENALTY_DEATH) != 0) { System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down."); Process.killProcess(Process.myPid()); System.exit(10); @@ -417,7 +461,7 @@ public final class StrictMode { * Called from Parcel.writeNoException() */ /* package */ static void writeGatheredViolationsToParcel(Parcel p) { - ArrayList<ApplicationErrorReport.CrashInfo> violations = gatheredViolations.get(); + ArrayList<ViolationInfo> violations = gatheredViolations.get(); if (violations == null) { p.writeInt(0); } else { @@ -439,35 +483,21 @@ public final class StrictMode { */ /* package */ static void readAndHandleBinderCallViolations(Parcel p) { // Our own stack trace to append - Exception e = new LogStackTrace(); StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); + new LogStackTrace().printStackTrace(new PrintWriter(sw)); String ourStack = sw.toString(); int policyMask = getThreadBlockingPolicy(); + boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0; int numViolations = p.readInt(); for (int i = 0; i < numViolations; ++i) { if (LOG_V) Log.d(TAG, "strict mode violation stacks read from binder call. i=" + i); - ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(p); - crashInfo.stackTrace += "# via Binder call with stack:\n" + ourStack; - - // Unlike the in-process violations in which case we - // trigger an error _before_ the thing occurs, in this - // case the violating thing has already occurred, so we - // can't use our heuristic of waiting for the next event - // loop idle cycle to measure the approximate violation - // duration. Instead, just skip that step and use -1 - // (unknown duration) for now. - // TODO: keep a thread-local on remote process of first - // violation time's uptimeMillis, and when writing that - // back out in Parcel reply, include in the header the - // violation time and use it here. - crashInfo.durationMillis = -1; - + ViolationInfo info = new ViolationInfo(p, !currentlyGathering); + info.crashInfo.stackTrace += "# via Binder call with stack:\n" + ourStack; BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); if (policy instanceof AndroidBlockGuardPolicy) { - ((AndroidBlockGuardPolicy) policy).handleViolation(crashInfo, policyMask); + ((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info); } } } @@ -483,4 +513,113 @@ public final class StrictMode { private static void onBinderStrictModePolicyChange(int newPolicy) { setBlockGuardPolicy(newPolicy); } + + /** + * Parcelable that gets sent in Binder call headers back to callers + * to report violations that happened during a cross-process call. + * + * @hide + */ + public static class ViolationInfo { + /** + * Stack and other stuff info. + */ + public final ApplicationErrorReport.CrashInfo crashInfo; + + /** + * The strict mode policy mask at the time of violation. + */ + public final int policy; + + /** + * The wall time duration of the violation, when known. -1 when + * not known. + */ + public int durationMillis = -1; + + /** + * Which violation number this was (1-based) since the last Looper loop, + * from the perspective of the root caller (if it crossed any processes + * via Binder calls). The value is 0 if the root caller wasn't on a Looper + * thread. + */ + public int violationNumThisLoop; + + /** + * The time (in terms of SystemClock.uptimeMillis()) that the + * violation occurred. + */ + public long violationUptimeMillis; + + /** + * Create an uninitialized instance of ViolationInfo + */ + public ViolationInfo() { + crashInfo = null; + policy = 0; + } + + /** + * Create an instance of ViolationInfo initialized from an exception. + */ + public ViolationInfo(Throwable tr, int policy) { + crashInfo = new ApplicationErrorReport.CrashInfo(tr); + violationUptimeMillis = SystemClock.uptimeMillis(); + this.policy = policy; + } + + /** + * Create an instance of ViolationInfo initialized from a Parcel. + */ + public ViolationInfo(Parcel in) { + this(in, false); + } + + /** + * Create an instance of ViolationInfo initialized from a Parcel. + * + * @param unsetGatheringBit if true, the caller is the root caller + * and the gathering penalty should be removed. + */ + public ViolationInfo(Parcel in, boolean unsetGatheringBit) { + crashInfo = new ApplicationErrorReport.CrashInfo(in); + int rawPolicy = in.readInt(); + if (unsetGatheringBit) { + policy = rawPolicy & ~PENALTY_GATHER; + } else { + policy = rawPolicy; + } + durationMillis = in.readInt(); + violationNumThisLoop = in.readInt(); + violationUptimeMillis = in.readLong(); + } + + /** + * Save a ViolationInfo instance to a parcel. + */ + public void writeToParcel(Parcel dest, int flags) { + crashInfo.writeToParcel(dest, flags); + dest.writeInt(policy); + dest.writeInt(durationMillis); + dest.writeInt(violationNumThisLoop); + dest.writeLong(violationUptimeMillis); + } + + + /** + * Dump a ViolationInfo instance to a Printer. + */ + public void dump(Printer pw, String prefix) { + crashInfo.dump(pw, prefix); + pw.println(prefix + "policy: " + policy); + if (durationMillis != -1) { + pw.println(prefix + "durationMillis: " + durationMillis); + } + if (violationNumThisLoop != 0) { + pw.println(prefix + "violationNumThisLoop: " + violationNumThisLoop); + } + pw.println(prefix + "violationUptimeMillis: " + violationUptimeMillis); + } + + } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java index cdaefc8..ad8d444 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java @@ -91,30 +91,30 @@ public class ConnectivityManagerMobileTest // DISCONNECTING, DISCONNECTED, UNKNOWN private void waitForNetworkState(int networkType, State expectedState, long timeout) { long startTime = System.currentTimeMillis(); - // In case the broadcast is already sent out, no need to wait - if (cmActivity.mCM.getNetworkInfo(networkType).getState() == expectedState) { - return; - } else { - while (true) { - if ((System.currentTimeMillis() - startTime) > timeout) { + while (true) { + if ((System.currentTimeMillis() - startTime) > timeout) { + if (cmActivity.mCM.getNetworkInfo(networkType).getState() != expectedState) { assertFalse("Wait for network state timeout", true); + } else { + // the broadcast has been sent out. the state has been changed. + return; + } + } + Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + + " to be " + expectedState.toString()); + synchronized (cmActivity.connectivityObject) { + try { + cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT); + } catch (InterruptedException e) { + e.printStackTrace(); } - Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + - " to be " + expectedState.toString()); - synchronized (cmActivity.connectivityObject) { - try { - cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if ((cmActivity.mNetworkInfo.getType() != networkType) || - (cmActivity.mNetworkInfo.getState() != expectedState)) { - Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() + - "is: " + cmActivity.mNetworkInfo.getState()); - continue; - } - break; + if ((cmActivity.mNetworkInfo.getType() != networkType) || + (cmActivity.mNetworkInfo.getState() != expectedState)) { + Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() + + "is: " + cmActivity.mNetworkInfo.getState()); + continue; } + break; } } } @@ -123,26 +123,26 @@ public class ConnectivityManagerMobileTest // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN private void waitForWifiState(int expectedState, long timeout) { long startTime = System.currentTimeMillis(); - if (cmActivity.mWifiState == expectedState) { - return; - } else { - while (true) { - if ((System.currentTimeMillis() - startTime) > timeout) { + while (true) { + if ((System.currentTimeMillis() - startTime) > timeout) { + if (cmActivity.mWifiState != expectedState) { assertFalse("Wait for Wifi state timeout", true); + } else { + return; + } + } + Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); + synchronized (cmActivity.wifiObject) { + try { + cmActivity.wifiObject.wait(5*1000); + } catch (InterruptedException e) { + e.printStackTrace(); } - Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); - synchronized (cmActivity.wifiObject) { - try { - cmActivity.wifiObject.wait(5*1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (cmActivity.mWifiState != expectedState) { - Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState()); - continue; - } - break; + if (cmActivity.mWifiState != expectedState) { + Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState()); + continue; } + break; } } } diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd index e3b9135..07af68d 100644 --- a/docs/html/guide/publishing/licensing.jd +++ b/docs/html/guide/publishing/licensing.jd @@ -154,7 +154,7 @@ published on that account. </p> <p>When the application receives a signed response, it uses the embedded public key to verify the data. The use of public key cryptography in the licensing service makes it possible for the application to detect responses that have been -tampered with or are spoofed.</p> +tampered with or that are spoofed.</p> <h4>Use of licensing in your application</h4> @@ -184,7 +184,7 @@ The LVL greatly simplifies the process of adding licensing to your application and helps ensure a more secure, robust implementation for your application. The LVL provides internal classes that handle most of the standard operations of a license query, such as contacting Android Market to initiate a license request -and decrypting and validating the responses. It also exposes key interfaces that +and verifying and validating the responses. It also exposes key interfaces that let you easily plug in your custom code for defining licensing policy and managing access as needed by your application. The key LVL interfaces are: </p> @@ -211,7 +211,7 @@ needs:</p> <li><a href="#ServerManagedPolicy">ServerManagedPolicy</a> is a flexible Policy that uses settings provided by the licensing server to manage response caching and access to the application while the device is offline (such as when the -user is on on an airplane). For most applications, the use of +user is on an airplane). For most applications, the use of ServerManagedPolicy is highly recommended. </li> <li><a href="#StrictPolicy">StrictPolicy</a> is a restrictive Policy that does not cache any response data and allows the application access <em>only</em> @@ -233,10 +233,10 @@ physical device.</p> <h4>Requirements and limitations</h4> <p>Android Market Licensing is designed to let you apply license controls to -applications that you publish through Android Market and that users download -from Market. The service is not designed to let you control access to -applications that are not published through Android Market or that are run -on devices that do not offer the Android Market client. </p> +applications that you publish through Android Market. The service is not +designed to let you control access to applications that are not published +through Android Market or that are run on devices that do not offer the Android +Market client. </p> <p>Here are some points to keep in mind as you implement licensing in your application: </p> @@ -267,14 +267,15 @@ you won't be able to upload a new version that uses licensing.</li> <p>Android Market Licensing is a flexible, secure mechanism for controlling access to your applications. It effectively replaces the copy-protection -mechanism and gives you wider distribution potential for your applications. </p> +mechanism offered on Android Market and gives you wider distribution +potential for your applications. </p> <ul> -<li>A limitation of copy protection is that applications using it can be -installed only on compatible devices that provide a secure internal storage -environment. For example, a copy-protected application cannot be downloaded -from Market to a device that provides root access, and the application -cannot be installed to a device's SD card. </li> +<li>A limitation of the legacy copy-protection mechanism on Android Market is +that applications using it can be installed only on compatible devices that +provide a secure internal storage environment. For example, a copy-protected +application cannot be downloaded from Market to a device that provides root +access, and the application cannot be installed to a device's SD card. </li> <li>With Android Market licensing, you can move to a license-based model in which access is not bound to the characteristics of the host device, but to your publisher account on Android Market and the licensing policy that you define. @@ -338,7 +339,7 @@ let you: </p> <ul> <li>Set up multiple "test accounts", identified by email address. The licensing server allows users signed into test accounts on a device or emulator to send -license checks and receive static test reponses.</li> +license checks and receive static test responses.</li> <li>Obtain the account's public key for licensing. When you are implementing licensing in an application, you must copy the public key string into the application.</li> @@ -402,7 +403,7 @@ href="#download-lvl">Downloading the LVL</a>.</p> <p>If you haven't done so, you need to download the Android SDK before you can develop Android applications. The SDK provides the tools that you need to build and debug Android applications, including applications that use Android Market -licensing. For complete information, including installation intructions, see +licensing. For complete information, including installation instructions, see the <a href="{@docRoot}sdk/index.html">Android SDK</a>. </p> <p>If you have already installed the SDK, make sure to update the @@ -472,7 +473,7 @@ However, it does provide: </p> <ul> <li>An Android Market background service that implements the -<code>ILicensingService</code> remote interface, so that your application can +ILicensingService remote interface, so that your application can send license checks over the network to the licensing server. </li> <li>A set of underlying account services that let you add an a Google account on the AVD and sign in using your publisher account or test account credentials. @@ -492,7 +493,7 @@ developing licensing on an emulator.</p> <img src="{@docRoot}images/licensing_gapis_8.png" style="text-align:left;margin-bottom:0;" /> <div style="margin:0 2em;padding:0"><strong>Figure 3.</strong> Google APIs Add-On, API 8 (release 2) or higher lets you debug and test your licensing -implemention in an emulator.</div> +implementation in an emulator.</div> </div> <p>To set up an emulator for adding licensing to an application, follow @@ -581,9 +582,9 @@ Licensing package contains the LVL and the LVL sample application. </div> <p>When the download is complete, the Android SDK and AVD Manager installs both the LVL library project and the example application into these directories: </p> -<p style="margin-left:2em"><code><<em>sdk</em>>/marketlicensing/library/</code> +<p style="margin-left:2em"><code><<em>sdk</em>>/market_licensing/library/</code> (the LVL library project)<br /> -<code><<em>sdk</em>>/marketlicensing/sample/</code> (the example +<code><<em>sdk</em>>/market_licensing/sample/</code> (the example application)</p> <p>If you aren't familiar with how to download components into your SDK, see the @@ -601,20 +602,21 @@ since it lets you reuse your licensing code across multiple applications and maintain it more easily over time. Note that the LVL is not designed to be compiled separately and added to an application as a static .jar file. </p> -<h4>Moving the library sources out location</h4> +<h4>Moving the library sources to a new location</h4> <p>Because you will be customizing the LVL sources to some extent, you should make sure to <em>move or copy</em> the library sources (the entire -directory at <code><<em>sdk</em>>/marketlicensing/library/</code>) -to a working directory outside of the SDK. You can then add the sources -in the working location to your source-code management system, rather -than those in the SDK.</p> +directory at <code><<em>sdk</em>>/market_licensing/library/</code>) +to a working directory outside of the SDK. You should then use the relocated +sources as your working set. If you are using a source-code management +system, add and track the sources that are in the working location rather +than those in default location in the SDK. </p> <p>Moving the library sources is important is because, when you later update the Market licensing package, the SDK installs the new files to the same location as the older files. Moving your working library files to a safe location ensures -that they won't be inadvertently overwritten when you download a new version of -the LVL.</p> +that your work won't be inadvertently overwritten should you download a new +version of the LVL.</p> <h4>Creating the LVL as a library project</h4> @@ -639,11 +641,11 @@ Other IDEs</a>, as appropriate for your environment.</p> <em>library project</em>. A library project is a type of development project that holds shared Android source code and resources. Other Android application projects can reference the library project and, at build time, include its -compiled sources in their <code>.apk</code> files. In the context of licensing, this means -that you can do most of your licensing development once, in a library project, -then include the library sources in your various application projects. In this -way, you can easily maintain a uniform implementation of licensing across all of -your projects and maintain it centrally. </p> +compiled sources in their <code>.apk</code> files. In the context of licensing, +this means that you can do most of your licensing development once, in a library +project, then include the library sources in your various application projects. +In this way, you can easily maintain a uniform implementation of licensing +across all of your projects and maintain it centrally. </p> <p>The LVL is provided as a configured library project — once you have downloaded it, you can start using it right away. </p> @@ -675,9 +677,8 @@ in Eclipse with ADT</a>.</p> <p>As an alternative to adding the LVL as a library project, you can copy the library sources directly into your application. To do so, copy (or import) the -directory -<code><<em>sdk</em>>/extras/marketlicensing/library/src/com</code> into -your application's <code>src/</code> directory.</p> +LVL's <code>library/src/com</code> directory into your application's +<code>src/</code> directory.</p> <p>If you add the LVL sources directly to your application, you can skip the next section and start working with the library, as described in <a @@ -688,10 +689,10 @@ href="#app-integration"></a>.</p> application</h3> <p>If you want to use the LVL sources as a library project, you need to add a -reference to the LVL library project in your project properties. This tells +reference to the LVL library project in your application project properties. This tells build tools to include the LVL library project sources in your application at -compile time. The process for adding a reference to a library project varies, -based on your development environment, as described below.</p> +compile time. The process for adding a reference to a library project depends +on your development environment, as described below.</p> <p> If you are developing in Eclipse with ADT, you should already have added the library project to your workspace, as described in the previous section. If you @@ -793,10 +794,10 @@ android:name="com.android.vending.CHECK_LICENSE"></code></p> </pre> <p class="note"><strong>Note:</strong> Currently, you cannot declare the -<code>CHECK_LICENSE</code> permission in the LVL's manifest, because the SDK -Tools will not merge it into the manifests of dependent applications. Instead, -you must declare the permission in the manifest of each dependent application. -</p> +<code>CHECK_LICENSE</code> permission in the LVL library project's manifest, +because the SDK Tools will not merge it into the manifests of dependent +applications. Instead, you must declare the permission in each dependent +application's manifest. </p> <h3 id="impl-Policy">Implementing a Policy</h3> @@ -856,7 +857,7 @@ licensing server or from cache) or other application-specific information. For example, your implementation of <code>allowAccess()</code> could take into account additional criteria, such as usage or other data retrieved from a backend server. In all cases, an implementation of <code>allowAccess()</code> -should only return <code>true</code> if there user is licensed to use the +should only return <code>true</code> if the user is licensed to use the application, as determined by the licensing server, or if there is a transient network or system problem that prevents the license check from completing. In such cases, your implementation can maintain a count of retry responses and @@ -866,7 +867,8 @@ provisionally allow access until the next license check is complete.</li> <p>To simplify the process of adding licensing to your application and to provide an illustration of how a Policy should be designed, the LVL includes -two full Policy implementations that you can use without modification:</p> +two full Policy implementations that you can use without modification or +adapt to your needs:</p> <ul> <li><a href="#ServerManagedPolicy">ServerManagedPolicy</a>, a flexible Policy @@ -886,7 +888,7 @@ the LVL sample application.</p> <p>In your licensing implementation, you can use one of the complete policies provided in the LVL (ServerManagedPolicy or StrictPolicy) or you can create a -custom Policy. For any type of custom policy, there are several important design +custom policy. For any type of custom policy, there are several important design points to understand and account for in your implementation.</p> <p>The licensing server applies general request limits to guard against overuse @@ -896,7 +898,7 @@ passed through to your application as a general server error. This means that no license response will be available to the user until the limit is reset, which can affect the user for an indefinite period.</p> -<p>If you are designing a custom Policy, we recommend that the Policy: +<p>If you are designing a custom policy, we recommend that the Policy: <ol> <!-- <li>Limits the number of points at which your app calls for a license check to the minimum. </li> --> @@ -904,12 +906,12 @@ to the minimum. </li> --> in local persistent storage.</li> <li>Returns the cached response for all license checks, for as long as the cached response is valid, rather than making a request to the licensing server. -Setting the response validity accrording to the server-provided <code>VT</code> +Setting the response validity according to the server-provided <code>VT</code> extra is highly recommended. See <a href="#extras">Server Response Extras</a> for more information.</li> -<li>Uses an exponential backoff period if retrying any requests the result in -errors. However, because the Android Market client automatically retries failed -requests, the Policy does not need to do so, in most cases.</li> +<li>Uses an exponential backoff period, if retrying any requests the result in +errors. Note that the Android Market client automatically retries failed +requests, so in most cases there is no need for your Policy to retry them.</li> <li>Provides for a "grace period" that allows the user to access your application for a limited time or number of uses, while a license check is being retried. The grace period benefits the user by allowing access until the next @@ -942,7 +944,7 @@ settings to the responses, to help the application manage licensing effectively. <p style="margin-top:.5em;">See <a href="#extras">Server Response Extras</a> for a list of settings and <code>ServerManagedPolicy.java</code> for information -about how a Policy can usethe extras.</p> +about how a Policy can use the extras.</p> </div> </div> @@ -965,8 +967,8 @@ responses.</p> server-provided settings as the basis for managing licensing across an application's refund period and through varying network and error conditions. When an application contacts the Android Market server for a license check, the -server appends several settings as key-value pairs in the license response -extras field. For example, the server provides recommended values for the +server appends several settings as key-value pairs in the extras field of certain +license response types. For example, the server provides recommended values for the application's license validity period, retry grace period, and maximum allowable retry count, among others. ServerManagedPolicy extracts the values from the license response in its <code>processServerResponse()</code> method and checks @@ -1019,8 +1021,7 @@ with the cached data and obtain access to the application.</p> means that they won't be able to access the application when there is no network (cell or wi-fi) connection available. Another side-effect is that your application will send more license check requests to the server, since using a -cached response is not possible. Depending on network conditions, this might -prove challenging for users also.</p> +cached response is not possible.</p> <p>Overall, this policy represents a tradeoff of some degree of user convenience for absolute security and control over access. Consider the tradeoff carefully @@ -1058,7 +1059,7 @@ data is persistent. </p> <p>Because the Policy will use stored license response data to determine whether to allow or disallow access to the application, it <em>must</em> ensure that any -stored data is secure and can not be reused or manipulated by a root user on a +stored data is secure and cannot be reused or manipulated by a root user on a device. Specifically, the Policy must always obfuscate the data before storing it, using a key that is unique for the application and device. Obfuscating using a key that is both application-specific and device-specific is critical, because @@ -1066,20 +1067,18 @@ it prevents the obfuscated data from being shared among applications and devices.</p> <p>The LVL assists the application with storing its license response data in a -secure, persistent manner. First, it provides an <code>Obfuscator</code> +secure, persistent manner. First, it provides an Obfuscator interface that lets your application supply the obfuscation algorithm of its choice for stored data. Building on that, the LVL provides the helper class -<code>PreferenceObfuscator</code>, which handles most of the work of calling the +PreferenceObfuscator, which handles most of the work of calling the application's Obfuscator class and reading and writing the obfuscated data in a SharedPreferences instance. </p> <p>The LVL provides a full Obfuscator implementation called -<code>AESObfuscator</code> that uses AES encryption to obfuscate data. You can -use <code>AESObfuscator</code> in your application without modification or you +AESObfuscator that uses AES encryption to obfuscate data. You can +use AESObfuscator in your application without modification or you can adapt it to your needs. For more information, see the next section.</p> -<p>Alternatively, you can write a custom Obfuscator based on your own code -or use an obfuscator program such as ProGuard for additional security.</p> <h4 id="AESObfuscator">AESObfuscator</h4> @@ -1118,14 +1117,16 @@ queries the system settings for the <code>android.Settings.Secure.ANDROID_ID</code>, which is unique to each device. </p> -<p>Note that, depending on the APIs you use to derive device-specific -information, your application might need to request additional permissions in -order to secure device-specific information. For example, if you query the -TelephonyManager to obtain the device IMEI or related data, your application -will also need to request the <code>android.permission.READ_PHONE_STATE</code> -permission in its manifest. Before requesting permissions in this way, consider -how doing so might affect your application or its filtering of your application -on Android Market (since some permissions can cause the SDK build tools to add +<p>Note that, depending on the APIs you use, your application might need to +request additional permissions in order to acquire device-specific information. +For example, to query the {@link android.telephony.TelephonyManager} to obtain +the device IMEI or related data, the application will also need to request the +<code>android.permission.READ_PHONE_STATE</code> permission in its manifest.</p> + +<p>Before requesting new permissions for the <em>sole purpose</em> of acquiring +device-specific information for use in your Obfuscator, consider +how doing so might affect your application or its filtering on Android Market +(since some permissions can cause the SDK build tools to add the associated <code><uses-feature></code>).</p> <p>Finally, construct an instance of AESObfuscator, passing the salt, @@ -1441,7 +1442,7 @@ licensing. </li> </ul> <p>If you are using ServerManagedPolicy, you won't need to access the class -directly, so you can instantiate it directly in the LicenseChecker constructor, +directly, so you can instantiate it in the LicenseChecker constructor, as shown in the example below. Note that you need to pass a reference to a new Obfuscator instance when you construct ServerManagedPolicy.</p> @@ -1510,7 +1511,7 @@ sample application calls <code>checkAccess()</code> from a <h4 id="account-key">Embed your public key for licensing</h4> <p>For each publisher account, the Android Market service automatically -generates a 2048-bit RSA public/private key pair that is used exlusively for +generates a 2048-bit RSA public/private key pair that is used exclusively for licensing. The key pair is uniquely associated with the publisher account and is shared across all applications that are published through the account. Although associated with a publisher account, the key pair is <em>not</em> the same as @@ -1562,14 +1563,14 @@ LicenseChecker to properly close any open IPC connection to the Android Market application's ILicensingService and removes any local references to the service and handler.</p> -<p>Failing to add the call the LicenseChecker's <code>onDestroy()</code> method +<p>Failing to call the LicenseChecker's <code>onDestroy()</code> method can lead to problems over the lifecycle of your application. For example, if the user changes screen orientation while a license check is active, the application {@link android.content.Context} is destroyed. If your application does not properly close the LicenseChecker's IPC connection, your application will crash when the response is received. Similarly, if the user exits your application while a license check is in progress, your application will crash when the -response is received, unless your application has properly called the +response is received, unless it has properly called the LicenseChecker's <code>onDestroy()</code> method to disconnect from the service. </p> @@ -1608,7 +1609,7 @@ user ID string extracted from the response.</p> required</strong> — the LicenseChecker class automatically uses a default implementation called NullDeviceLimiter. As the name suggests, NullDeviceLimiter is a "no-op" class whose <code>allowDeviceAccess()</code> method simply returns -a LICENSED response for all users and devices. </p> +a <code>LICENSED</code> response for all users and devices. </p> <div style="border-left:4px solid #FFCF00;margin:1em;padding: 0 0 0 .5em"> <p><strong>Caution:</strong> Per-device licensing is <em>not recommended for @@ -1634,15 +1635,15 @@ error conditions.</p> <ul> <li>A "Test response" configuration in your publisher account that lets you -control the licensing response returned, when the server processes a license -check for an uploaded application from the publisher account or a test -account.</li> -<li>An optional set of test accounts that will receive the configured test -response when checking the license of an application that you have uploaded +set the static licensing response returned, when the server processes a +license check for an application uploaded to the publisher account, from a user +signed in to the publisher account or a test account.</li> +<li>An optional set of test accounts that will receive the static test +response when they check the license of an application that you have uploaded (regardless whether the application is published or not).</li> <li>A runtime environment for the application that includes the Android Market application or Google APIs Add-On, on which the user is signed in to the -publisher account or one of the test accounts. +publisher account or one of the test accounts.</li> </ul> <p>Setting up the test environment properly involves:</p> @@ -1659,11 +1660,12 @@ publisher account or one of the test accounts. <h3 id="test-response">Setting test responses for license checks</h3> <p>Android Market provides a configuration setting in your publisher account -that lets you override the normal processing of a license check for an -application you have uploaded and return a specified response code. The setting -is for testing only and applies <em>only</em> to license checks for applications -that you have uploaded. For other users (users not signed in to test accounts), -the server always processes license checks according to normal rules. </p> +that lets you override the normal processing of a license check and return a +specified static response code. The setting is for testing only and applies +<em>only</em> to license checks for applications that you have uploaded, made by +any user signed in to an emulator or device using the credentials of the +publisher account or a registered test account. For other users, the server +always processes license checks according to normal rules. </p> <p>To set a test response for your account, sign in to your publisher account and click "Edit Profile". In the Edit Profile page, locate the Test Response @@ -1672,9 +1674,9 @@ valid server response codes to control the response or condition you want to test in your application.</p> <p>In general, you should make sure to test your application's licensing -implementation with every response code available in the Test Response" menu. +implementation with every response code available in the Test Response menu. For a description of the codes, see <a href="#server-response-codes">Server -Response Codes</a>, below.</p> +Response Codes</a> in the Appendix of this document.</p> <div style="margin-bottom:2em;" id="licensing_test_response"> @@ -1688,10 +1690,11 @@ Test Response menu.</div> that is, it applies not to a single application, but to <em>all</em> applications associated with the publisher account. If you are testing multiple applications at once, changing the test response will affect all of those -applications on their next license check.</p> +applications on their next license check (if the user is signed into +the emulator or device using the publisher account or a test account).</p> -<p>Finally, before you can successfully send these test responses to your -application, you must sign in to the device or emulator on which the application +<p>Before you can successfully receive a test response for a license check, +you must sign in to the device or emulator on which the application is installed, and from which it is querying the server. Specifically, you must sign using either your publisher account or one of the test accounts that you have set up. For more information about test accounts, see the next section.</p> @@ -1799,7 +1802,7 @@ upload a new version if the local application increments the <p>The licensing server handles static test responses in the normal way, including signing the license response data, adding extras parameters, and so on. To support developers who are implementing licensing using test accounts, -rather than having access to the publisher account, you will need to distribute +rather than the publisher account, you will need to distribute your public key to them. Developers without access to the publisher site do not have access to your public key, and without the key they won't be able to verify license responses. </p> @@ -2176,7 +2179,7 @@ that is not installed on the device. </td> <td>No </td> <td></td> <td><em>Do not retry the license check.</em> -<p style="margin-top:.5em;">Typically caused by a developement error.</p> +<p style="margin-top:.5em;">Typically caused by a development error.</p> </td> </tr> <tr> @@ -2187,7 +2190,7 @@ application. </td> <td>No </td> <td></td> <td><em>Do not retry the license check.</em> -<p style="margin-top:.5em;">Typically caused by a developement error.</p> +<p style="margin-top:.5em;">Typically caused by a development error.</p> </td> </tr> <tr> @@ -2295,7 +2298,7 @@ the server sets a validity period as follows:</p> <ul> <li>For a paid application, the server sets the initial license validity period -so that the license reponse remains valid for as long as the application is +so that the license response remains valid for as long as the application is refundable. A licensing Policy in the application may cache the result of the initial license check and does not need to recheck the license until the validity period has expired.</li> diff --git a/docs/html/index.jd b/docs/html/index.jd index 01940e8..f37a122 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -163,12 +163,11 @@ href="{@docRoot}resources/dashboard/platform-versions.html">Learn more »</ 'img':"devphone-large.png", 'title':"Android Dev Phones", 'desc': "<p>Run and debug your Android applications directly on one of these " - + "device. Modify and rebuild the Android operating system, and flash it onto " - + "the phone. The Android Dev Phones are carrier independent, and available for " - + "purchase by any developer registered with <a " - + "href='http://market.android.com/publish'>Android Market</a>.</p><p><a " - + "href='/guide/developing/device.html#dev-phone-1'>Learn more about the " - + "Android Dev Phones »</a></p>" + + "devices. Modify and rebuild the Android operating system, and flash it onto " + + "the phone. The Android Dev Phones are carrier-independent, and available for " + + "purchase by developers through their Android Market publisher accounts.</p><p> " + + "<a href='http://market.android.com/publish'>Visit Android Market " + + "to learn more »</a></p>" }, 'mapskey': { diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index d7ec8ea..6bf1bfa 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -210,7 +210,7 @@ public: * IMPORTANT INVARIANT: * Because the policy and dispatcher can potentially block or cause re-entrance into * the input reader, the input reader never calls into other components while holding - * an exclusive internal lock. + * an exclusive internal lock whenever re-entrance can happen. */ class InputReader : public InputReaderInterface, private InputReaderContext { public: @@ -414,6 +414,8 @@ public: virtual int32_t getMetaState(); private: + Mutex mLock; + struct KeyDown { int32_t keyCode; int32_t scanCode; @@ -423,17 +425,22 @@ private: uint32_t mSources; int32_t mKeyboardType; - Vector<KeyDown> mKeyDowns; // keys that are down - int32_t mMetaState; - nsecs_t mDownTime; // time of most recent key down + struct LockedState { + Vector<KeyDown> keyDowns; // keys that are down + int32_t metaState; + nsecs_t downTime; // time of most recent key down + } mLocked; - void initialize(); + void initializeLocked(); bool isKeyboardOrGamepadKey(int32_t scanCode); + void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags); + void applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, + bool down, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime); - ssize_t findKeyDown(int32_t scanCode); + ssize_t findKeyDownLocked(int32_t scanCode); }; @@ -451,6 +458,8 @@ private: // Amount that trackball needs to move in order to generate a key event. static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + Mutex mLock; + int32_t mAssociatedDisplayId; struct Accumulator { @@ -475,17 +484,21 @@ private: } } mAccumulator; - bool mDown; - nsecs_t mDownTime; - float mXScale; float mYScale; float mXPrecision; float mYPrecision; - void initialize(); + struct LockedState { + bool down; + nsecs_t downTime; + } mLocked; + + void initializeLocked(); void sync(nsecs_t when); + void applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction, + PointerCoords* pointerCoords, nsecs_t downTime); }; @@ -509,6 +522,8 @@ protected: * (This is limited by our use of BitSet32 to track pointer assignments.) */ static const uint32_t MAX_POINTER_ID = 31; + Mutex mLock; + struct VirtualKey { int32_t keyCode; int32_t scanCode; @@ -561,7 +576,6 @@ protected: }; int32_t mAssociatedDisplayId; - Vector<VirtualKey> mVirtualKeys; // Immutable configuration parameters. struct Parameters { @@ -583,67 +597,65 @@ protected: RawAbsoluteAxisInfo orientation; } mAxes; - // The surface orientation and width and height set by configureSurface(). - int32_t mSurfaceOrientation; - int32_t mSurfaceWidth, mSurfaceHeight; - - // Translation and scaling factors, orientation-independent. - int32_t mXOrigin; - float mXScale; - float mXPrecision; - - int32_t mYOrigin; - float mYScale; - float mYPrecision; - - int32_t mPressureOrigin; - float mPressureScale; - - int32_t mSizeOrigin; - float mSizeScale; - - float mOrientationScale; - - // Oriented motion ranges for input device info. - struct OrientedRanges { - InputDeviceInfo::MotionRange x; - InputDeviceInfo::MotionRange y; - InputDeviceInfo::MotionRange pressure; - InputDeviceInfo::MotionRange size; - InputDeviceInfo::MotionRange touchMajor; - InputDeviceInfo::MotionRange touchMinor; - InputDeviceInfo::MotionRange toolMajor; - InputDeviceInfo::MotionRange toolMinor; - InputDeviceInfo::MotionRange orientation; - } mOrientedRanges; - - // Oriented dimensions and precision. - float mOrientedSurfaceWidth, mOrientedSurfaceHeight; - float mOrientedXPrecision, mOrientedYPrecision; - - // The touch data of the current sample being processed. + // Current and previous touch sample data. TouchData mCurrentTouch; - - // The touch data of the previous sample that was processed. This is updated - // incrementally while the current sample is being processed. TouchData mLastTouch; // The time the primary pointer last went down. nsecs_t mDownTime; - struct CurrentVirtualKeyState { - bool down; - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; - } mCurrentVirtualKey; - - // Lock for virtual key state. - Mutex mVirtualKeyLock; // methods use "Lvk" suffix + struct LockedState { + Vector<VirtualKey> virtualKeys; + + // The surface orientation and width and height set by configureSurfaceLocked(). + int32_t surfaceOrientation; + int32_t surfaceWidth, surfaceHeight; + + // Translation and scaling factors, orientation-independent. + int32_t xOrigin; + float xScale; + float xPrecision; + + int32_t yOrigin; + float yScale; + float yPrecision; + + int32_t pressureOrigin; + float pressureScale; + + int32_t sizeOrigin; + float sizeScale; + + float orientationScale; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + InputDeviceInfo::MotionRange size; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + InputDeviceInfo::MotionRange orientation; + } orientedRanges; + + // Oriented dimensions and precision. + float orientedSurfaceWidth, orientedSurfaceHeight; + float orientedXPrecision, orientedYPrecision; + + struct CurrentVirtualKeyState { + bool down; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } currentVirtualKey; + } mLocked; virtual void configureAxes(); - virtual bool configureSurface(); - virtual void configureVirtualKeys(); + virtual bool configureSurfaceLocked(); + virtual void configureVirtualKeysLocked(); enum TouchResult { // Dispatch the touch normally. @@ -696,15 +708,19 @@ private: uint64_t distance : 48; // squared distance }; - void initialize(); + void initializeLocked(); TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, BitSet32 idBits, uint32_t changedId, int32_t motionEventAction); - bool isPointInsideSurface(int32_t x, int32_t y); - const VirtualKey* findVirtualKeyHitLvk(int32_t x, int32_t y); + void applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags, + int32_t keyCode, int32_t scanCode, nsecs_t downTime); + + bool isPointInsideSurfaceLocked(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y); bool applyBadTouchFilter(); bool applyJumpyTouchFilter(); diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 56e2977..6618702 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -713,15 +713,15 @@ KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associated uint32_t sources, int32_t keyboardType) : InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources), mKeyboardType(keyboardType) { - initialize(); + initializeLocked(); } KeyboardInputMapper::~KeyboardInputMapper() { } -void KeyboardInputMapper::initialize() { - mMetaState = AMETA_NONE; - mDownTime = 0; +void KeyboardInputMapper::initializeLocked() { + mLocked.metaState = AMETA_NONE; + mLocked.downTime = 0; } uint32_t KeyboardInputMapper::getSources() { @@ -735,17 +735,27 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { } void KeyboardInputMapper::reset() { - // Synthesize key up event on reset if keys are currently down. - while (! mKeyDowns.isEmpty()) { - const KeyDown& keyDown = mKeyDowns.top(); + for (;;) { + int32_t keyCode, scanCode; + { // acquire lock + AutoMutex _l(mLock); + + // Synthesize key up event on reset if keys are currently down. + if (mLocked.keyDowns.isEmpty()) { + initializeLocked(); + break; // done + } + + const KeyDown& keyDown = mLocked.keyDowns.top(); + keyCode = keyDown.keyCode; + scanCode = keyDown.scanCode; + } // release lock + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); - processKey(when, false, keyDown.keyCode, keyDown.scanCode, 0); + processKey(when, false, keyCode, scanCode, 0); } InputMapper::reset(); - - // Reinitialize. - initialize(); getContext()->updateGlobalMetaState(); } @@ -768,56 +778,76 @@ bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); } -void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, - uint32_t policyFlags) { - if (down) { - // Rotate key codes according to orientation. - if (mAssociatedDisplayId >= 0) { - int32_t orientation; - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { - return; +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, + int32_t scanCode, uint32_t policyFlags) { + int32_t newMetaState; + nsecs_t downTime; + bool metaStateChanged = false; + + { // acquire lock + AutoMutex _l(mLock); + + if (down) { + // Rotate key codes according to orientation if needed. + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. + if (mAssociatedDisplayId >= 0) { + int32_t orientation; + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + + keyCode = rotateKeyCode(keyCode, orientation); } - keyCode = rotateKeyCode(keyCode, orientation); - } + // Add key down. + ssize_t keyDownIndex = findKeyDownLocked(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mLocked.keyDowns.top().keyCode; + } else { + // key down + mLocked.keyDowns.push(); + KeyDown& keyDown = mLocked.keyDowns.editTop(); + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + } - // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns.top().keyCode; + mLocked.downTime = when; } else { - // key down - mKeyDowns.push(); - KeyDown& keyDown = mKeyDowns.editTop(); - keyDown.keyCode = keyCode; - keyDown.scanCode = scanCode; + // Remove key down. + ssize_t keyDownIndex = findKeyDownLocked(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mLocked.keyDowns.top().keyCode; + mLocked.keyDowns.removeAt(size_t(keyDownIndex)); + } else { + // key was not actually down + LOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().string(), keyCode, scanCode); + return; + } } - } else { - // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns.top().keyCode; - mKeyDowns.removeAt(size_t(keyDownIndex)); - } else { - // key was not actually down - LOGI("Dropping key up from device %s because the key was not down. " - "keyCode=%d, scanCode=%d", - getDeviceName().string(), keyCode, scanCode); - return; + + int32_t oldMetaState = mLocked.metaState; + newMetaState = updateMetaState(keyCode, down, oldMetaState); + if (oldMetaState != newMetaState) { + mLocked.metaState = newMetaState; + metaStateChanged = true; } - } - int32_t oldMetaState = mMetaState; - int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState); - if (oldMetaState != newMetaState) { - mMetaState = newMetaState; + downTime = mLocked.downTime; + } // release lock + + if (metaStateChanged) { getContext()->updateGlobalMetaState(); } - /* Apply policy. */ + applyPolicyAndDispatch(when, policyFlags, down, keyCode, scanCode, newMetaState, downTime); +} +void KeyboardInputMapper::applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, bool down, + int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(), down, keyCode, scanCode, policyFlags); @@ -825,30 +855,20 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, i return; // event dropped } - /* Enqueue key event for dispatch. */ - - int32_t keyEventAction; - if (down) { - mDownTime = when; - keyEventAction = AKEY_EVENT_ACTION_DOWN; - } else { - keyEventAction = AKEY_EVENT_ACTION_UP; - } - + int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP; int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM; if (policyFlags & POLICY_FLAG_WOKE_HERE) { keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE; } getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, - keyEventAction, keyEventFlags, keyCode, scanCode, - mMetaState, mDownTime); + keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); } -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { - size_t n = mKeyDowns.size(); +ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) { + size_t n = mLocked.keyDowns.size(); for (size_t i = 0; i < n; i++) { - if (mKeyDowns[i].scanCode == scanCode) { + if (mLocked.keyDowns[i].scanCode == scanCode) { return i; } } @@ -869,7 +889,10 @@ bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numC } int32_t KeyboardInputMapper::getMetaState() { - return mMetaState; + { // acquire lock + AutoMutex _l(mLock); + return mLocked.metaState; + } // release lock } @@ -882,7 +905,7 @@ TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associat mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - initialize(); + initializeLocked(); } TrackballInputMapper::~TrackballInputMapper() { @@ -899,26 +922,33 @@ void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) { info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale); } -void TrackballInputMapper::initialize() { +void TrackballInputMapper::initializeLocked() { mAccumulator.clear(); - mDown = false; - mDownTime = 0; + mLocked.down = false; + mLocked.downTime = 0; } void TrackballInputMapper::reset() { - // Synthesize trackball button up event on reset if trackball button is currently down. - if (mDown) { + for (;;) { + { // acquire lock + AutoMutex _l(mLock); + + if (! mLocked.down) { + initializeLocked(); + break; // done + } + } // release lock + + // Synthesize trackball button up event on reset. nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); - mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; + mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE; mAccumulator.btnMouse = false; sync(when); + mAccumulator.clear(); } InputMapper::reset(); - - // Reinitialize. - initialize(); } void TrackballInputMapper::process(const RawEvent* rawEvent) { @@ -962,33 +992,79 @@ void TrackballInputMapper::process(const RawEvent* rawEvent) { } void TrackballInputMapper::sync(nsecs_t when) { - /* Get display properties so for rotation based on display orientation. */ + int motionEventAction; + PointerCoords pointerCoords; + nsecs_t downTime; + { // acquire lock + AutoMutex _l(mLock); - int32_t orientation; - if (mAssociatedDisplayId >= 0) { - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { - return; - } - } else { - orientation = InputReaderPolicyInterface::ROTATION_0; - } + uint32_t fields = mAccumulator.fields; + bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE; - /* Update saved trackball state */ + if (downChanged) { + if (mAccumulator.btnMouse) { + mLocked.down = true; + mLocked.downTime = when; + } else { + mLocked.down = false; + } + } - uint32_t fields = mAccumulator.fields; - bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE; + downTime = mLocked.downTime; + float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f; + float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f; - if (downChanged) { - if (mAccumulator.btnMouse) { - mDown = true; - mDownTime = when; + if (downChanged) { + motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; } else { - mDown = false; + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } + + pointerCoords.x = x; + pointerCoords.y = y; + pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f; + pointerCoords.size = 0; + pointerCoords.touchMajor = 0; + pointerCoords.touchMinor = 0; + pointerCoords.toolMajor = 0; + pointerCoords.toolMinor = 0; + pointerCoords.orientation = 0; + + if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) { + // Rotate motion based on display orientation if needed. + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. + int32_t orientation; + if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { + return; + } + + float temp; + switch (orientation) { + case InputReaderPolicyInterface::ROTATION_90: + temp = pointerCoords.x; + pointerCoords.x = pointerCoords.y; + pointerCoords.y = - temp; + break; + + case InputReaderPolicyInterface::ROTATION_180: + pointerCoords.x = - pointerCoords.x; + pointerCoords.y = - pointerCoords.y; + break; + + case InputReaderPolicyInterface::ROTATION_270: + temp = pointerCoords.x; + pointerCoords.x = - pointerCoords.y; + pointerCoords.y = temp; + break; + } } - } + } // release lock - /* Apply policy */ + applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime); +} +void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction, + PointerCoords* pointerCoords, nsecs_t downTime) { uint32_t policyFlags = 0; int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); @@ -996,62 +1072,24 @@ void TrackballInputMapper::sync(nsecs_t when) { return; // event dropped } - /* Enqueue motion event for dispatch. */ - - int32_t motionEventAction; - if (downChanged) { - motionEventAction = mDown ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } - + int32_t metaState = mContext->getGlobalMetaState(); int32_t pointerId = 0; - PointerCoords pointerCoords; - pointerCoords.x = fields & Accumulator::FIELD_REL_X - ? mAccumulator.relX * mXScale : 0; - pointerCoords.y = fields & Accumulator::FIELD_REL_Y - ? mAccumulator.relY * mYScale : 0; - pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise. - pointerCoords.size = 0; - pointerCoords.touchMajor = 0; - pointerCoords.touchMinor = 0; - pointerCoords.toolMajor = 0; - pointerCoords.toolMinor = 0; - pointerCoords.orientation = 0; - - float temp; - switch (orientation) { - case InputReaderPolicyInterface::ROTATION_90: - temp = pointerCoords.x; - pointerCoords.x = pointerCoords.y; - pointerCoords.y = - temp; - break; - - case InputReaderPolicyInterface::ROTATION_180: - pointerCoords.x = - pointerCoords.x; - pointerCoords.y = - pointerCoords.y; - break; - case InputReaderPolicyInterface::ROTATION_270: - temp = pointerCoords.x; - pointerCoords.x = - pointerCoords.y; - pointerCoords.y = temp; - break; - } - - int32_t metaState = mContext->getGlobalMetaState(); getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags, motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, & pointerId, & pointerCoords, mXPrecision, mYPrecision, mDownTime); + 1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime); } // --- TouchInputMapper --- TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId), - mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) { - initialize(); + InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { + mLocked.surfaceOrientation = -1; + mLocked.surfaceWidth = -1; + mLocked.surfaceHeight = -1; + + initializeLocked(); } TouchInputMapper::~TouchInputMapper() { @@ -1064,26 +1102,29 @@ uint32_t TouchInputMapper::getSources() { void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - // FIXME: Should ensure the surface information is up to date so that orientation changes - // are noticed immediately. Unfortunately we will need to add some extra locks here - // to prevent race conditions. - // configureSurface(); + { // acquire lock + AutoMutex _l(mLock); + + // Ensure surface information is up to date so that orientation changes are + // noticed immediately. + configureSurfaceLocked(); - info->addMotionRange(AINPUT_MOTION_RANGE_X, mOrientedRanges.x); - info->addMotionRange(AINPUT_MOTION_RANGE_Y, mOrientedRanges.y); - info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mOrientedRanges.pressure); - info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mOrientedRanges.size); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mOrientedRanges.touchMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mOrientedRanges.touchMinor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mOrientedRanges.toolMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mOrientedRanges.toolMinor); - info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mOrientedRanges.orientation); + info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x); + info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y); + info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mLocked.orientedRanges.pressure); + info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mLocked.orientedRanges.size); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mLocked.orientedRanges.touchMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mLocked.orientedRanges.touchMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mLocked.orientedRanges.toolMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mLocked.orientedRanges.toolMinor); + info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mLocked.orientedRanges.orientation); + } // release lock } -void TouchInputMapper::initialize() { +void TouchInputMapper::initializeLocked() { + mCurrentTouch.clear(); mLastTouch.clear(); mDownTime = 0; - mCurrentVirtualKey.down = false; for (uint32_t i = 0; i < MAX_POINTERS; i++) { mAveragingTouchFilter.historyStart[i] = 0; @@ -1091,6 +1132,8 @@ void TouchInputMapper::initialize() { } mJumpyTouchFilter.jumpyPointsDropped = 0; + + mLocked.currentVirtualKey.down = false; } void TouchInputMapper::configure() { @@ -1104,48 +1147,52 @@ void TouchInputMapper::configure() { // Configure absolute axis information. configureAxes(); - // Configure pressure factors. - if (mAxes.pressure.valid) { - mPressureOrigin = mAxes.pressure.minValue; - mPressureScale = 1.0f / mAxes.pressure.getRange(); - } else { - mPressureOrigin = 0; - mPressureScale = 1.0f; - } + { // acquire lock + AutoMutex _l(mLock); - mOrientedRanges.pressure.min = 0.0f; - mOrientedRanges.pressure.max = 1.0f; - mOrientedRanges.pressure.flat = 0.0f; - mOrientedRanges.pressure.fuzz = mPressureScale; + // Configure pressure factors. + if (mAxes.pressure.valid) { + mLocked.pressureOrigin = mAxes.pressure.minValue; + mLocked.pressureScale = 1.0f / mAxes.pressure.getRange(); + } else { + mLocked.pressureOrigin = 0; + mLocked.pressureScale = 1.0f; + } - // Configure size factors. - if (mAxes.size.valid) { - mSizeOrigin = mAxes.size.minValue; - mSizeScale = 1.0f / mAxes.size.getRange(); - } else { - mSizeOrigin = 0; - mSizeScale = 1.0f; - } + mLocked.orientedRanges.pressure.min = 0.0f; + mLocked.orientedRanges.pressure.max = 1.0f; + mLocked.orientedRanges.pressure.flat = 0.0f; + mLocked.orientedRanges.pressure.fuzz = mLocked.pressureScale; - mOrientedRanges.size.min = 0.0f; - mOrientedRanges.size.max = 1.0f; - mOrientedRanges.size.flat = 0.0f; - mOrientedRanges.size.fuzz = mSizeScale; + // Configure size factors. + if (mAxes.size.valid) { + mLocked.sizeOrigin = mAxes.size.minValue; + mLocked.sizeScale = 1.0f / mAxes.size.getRange(); + } else { + mLocked.sizeOrigin = 0; + mLocked.sizeScale = 1.0f; + } - // Configure orientation factors. - if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) { - mOrientationScale = float(M_PI_2) / mAxes.orientation.maxValue; - } else { - mOrientationScale = 0.0f; - } + mLocked.orientedRanges.size.min = 0.0f; + mLocked.orientedRanges.size.max = 1.0f; + mLocked.orientedRanges.size.flat = 0.0f; + mLocked.orientedRanges.size.fuzz = mLocked.sizeScale; - mOrientedRanges.orientation.min = - M_PI_2; - mOrientedRanges.orientation.max = M_PI_2; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = mOrientationScale; + // Configure orientation factors. + if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) { + mLocked.orientationScale = float(M_PI_2) / mAxes.orientation.maxValue; + } else { + mLocked.orientationScale = 0.0f; + } + + mLocked.orientedRanges.orientation.min = - M_PI_2; + mLocked.orientedRanges.orientation.max = M_PI_2; + mLocked.orientedRanges.orientation.flat = 0; + mLocked.orientedRanges.orientation.fuzz = mLocked.orientationScale; - // Configure surface dimensions and orientation. - configureSurface(); + // Configure surface dimensions and orientation. + configureSurfaceLocked(); + } // release lock } void TouchInputMapper::configureAxes() { @@ -1160,11 +1207,12 @@ void TouchInputMapper::configureAxes() { mAxes.orientation.valid = false; } -bool TouchInputMapper::configureSurface() { +bool TouchInputMapper::configureSurfaceLocked() { // Update orientation and dimensions if needed. int32_t orientation; int32_t width, height; if (mAssociatedDisplayId >= 0) { + // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) { return false; } @@ -1174,150 +1222,152 @@ bool TouchInputMapper::configureSurface() { height = mAxes.y.getRange(); } - bool orientationChanged = mSurfaceOrientation != orientation; + bool orientationChanged = mLocked.surfaceOrientation != orientation; if (orientationChanged) { - mSurfaceOrientation = orientation; + mLocked.surfaceOrientation = orientation; } - bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height; + bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; if (sizeChanged) { - mSurfaceWidth = width; - mSurfaceHeight = height; + mLocked.surfaceWidth = width; + mLocked.surfaceHeight = height; // Compute size-dependent translation and scaling factors and place virtual keys. if (mAxes.x.valid && mAxes.y.valid) { - mXOrigin = mAxes.x.minValue; - mYOrigin = mAxes.y.minValue; + mLocked.xOrigin = mAxes.x.minValue; + mLocked.yOrigin = mAxes.y.minValue; LOGI("Device configured: id=0x%x, name=%s (display size was changed)", getDeviceId(), getDeviceName().string()); - mXScale = float(width) / mAxes.x.getRange(); - mYScale = float(height) / mAxes.y.getRange(); - mXPrecision = 1.0f / mXScale; - mYPrecision = 1.0f / mYScale; + mLocked.xScale = float(width) / mAxes.x.getRange(); + mLocked.yScale = float(height) / mAxes.y.getRange(); + mLocked.xPrecision = 1.0f / mLocked.xScale; + mLocked.yPrecision = 1.0f / mLocked.yScale; - configureVirtualKeys(); + configureVirtualKeysLocked(); } else { - mXOrigin = 0; - mYOrigin = 0; - mXScale = 1.0f; - mYScale = 1.0f; - mXPrecision = 1.0f; - mYPrecision = 1.0f; + mLocked.xOrigin = 0; + mLocked.yOrigin = 0; + mLocked.xScale = 1.0f; + mLocked.yScale = 1.0f; + mLocked.xPrecision = 1.0f; + mLocked.yPrecision = 1.0f; } // Configure touch and tool area ranges. float diagonal = sqrt(float(width * width + height * height)); - float diagonalFuzz = sqrt(mXScale * mXScale + mYScale * mYScale); + float diagonalFuzz = sqrt(mLocked.xScale * mLocked.xScale + + mLocked.yScale * mLocked.yScale); - mOrientedRanges.touchMajor.min = 0.0f; - mOrientedRanges.touchMajor.max = diagonal; - mOrientedRanges.touchMajor.flat = 0.0f; - mOrientedRanges.touchMajor.fuzz = diagonalFuzz; - mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + InputDeviceInfo::MotionRange area; + area.min = 0.0f; + area.max = diagonal; + area.flat = 0.0f; + area.fuzz = diagonalFuzz; - mOrientedRanges.toolMinor = mOrientedRanges.toolMajor = mOrientedRanges.touchMajor; + mLocked.orientedRanges.touchMajor = area; + mLocked.orientedRanges.touchMinor = area; + + mLocked.orientedRanges.toolMajor = area; + mLocked.orientedRanges.toolMinor = area; } if (orientationChanged || sizeChanged) { // Compute oriented surface dimensions, precision, and scales. float orientedXScale, orientedYScale; - switch (mSurfaceOrientation) { + switch (mLocked.surfaceOrientation) { case InputReaderPolicyInterface::ROTATION_90: case InputReaderPolicyInterface::ROTATION_270: - mOrientedSurfaceWidth = mSurfaceHeight; - mOrientedSurfaceHeight = mSurfaceWidth; - mOrientedXPrecision = mYPrecision; - mOrientedYPrecision = mXPrecision; - orientedXScale = mYScale; - orientedYScale = mXScale; + mLocked.orientedSurfaceWidth = mLocked.surfaceHeight; + mLocked.orientedSurfaceHeight = mLocked.surfaceWidth; + mLocked.orientedXPrecision = mLocked.yPrecision; + mLocked.orientedYPrecision = mLocked.xPrecision; + orientedXScale = mLocked.yScale; + orientedYScale = mLocked.xScale; break; default: - mOrientedSurfaceWidth = mSurfaceWidth; - mOrientedSurfaceHeight = mSurfaceHeight; - mOrientedXPrecision = mXPrecision; - mOrientedYPrecision = mYPrecision; - orientedXScale = mXScale; - orientedYScale = mYScale; + mLocked.orientedSurfaceWidth = mLocked.surfaceWidth; + mLocked.orientedSurfaceHeight = mLocked.surfaceHeight; + mLocked.orientedXPrecision = mLocked.xPrecision; + mLocked.orientedYPrecision = mLocked.yPrecision; + orientedXScale = mLocked.xScale; + orientedYScale = mLocked.yScale; break; } // Configure position ranges. - mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mOrientedSurfaceWidth; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = orientedXScale; + mLocked.orientedRanges.x.min = 0; + mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth; + mLocked.orientedRanges.x.flat = 0; + mLocked.orientedRanges.x.fuzz = orientedXScale; - mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mOrientedSurfaceHeight; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = orientedYScale; + mLocked.orientedRanges.y.min = 0; + mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight; + mLocked.orientedRanges.y.flat = 0; + mLocked.orientedRanges.y.fuzz = orientedYScale; } return true; } -void TouchInputMapper::configureVirtualKeys() { +void TouchInputMapper::configureVirtualKeysLocked() { assert(mAxes.x.valid && mAxes.y.valid); + // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock. Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); - { // acquire virtual key lock - AutoMutex _l(mVirtualKeyLock); - - mVirtualKeys.clear(); + mLocked.virtualKeys.clear(); - if (virtualKeyDefinitions.size() == 0) { - return; - } + if (virtualKeyDefinitions.size() == 0) { + return; + } - mVirtualKeys.setCapacity(virtualKeyDefinitions.size()); + mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size()); - int32_t touchScreenLeft = mAxes.x.minValue; - int32_t touchScreenTop = mAxes.y.minValue; - int32_t touchScreenWidth = mAxes.x.getRange(); - int32_t touchScreenHeight = mAxes.y.getRange(); + int32_t touchScreenLeft = mAxes.x.minValue; + int32_t touchScreenTop = mAxes.y.minValue; + int32_t touchScreenWidth = mAxes.x.getRange(); + int32_t touchScreenHeight = mAxes.y.getRange(); - for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { - const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = - virtualKeyDefinitions[i]; + for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { + const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = + virtualKeyDefinitions[i]; - mVirtualKeys.add(); - VirtualKey& virtualKey = mVirtualKeys.editTop(); + mLocked.virtualKeys.add(); + VirtualKey& virtualKey = mLocked.virtualKeys.editTop(); - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - uint32_t flags; - if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, - & keyCode, & flags)) { - LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); - mVirtualKeys.pop(); // drop the key - continue; - } + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + uint32_t flags; + if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, + & keyCode, & flags)) { + LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + mLocked.virtualKeys.pop(); // drop the key + continue; + } - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; - virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) - * touchScreenWidth / mSurfaceWidth + touchScreenLeft; - virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) - * touchScreenWidth / mSurfaceWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) - * touchScreenHeight / mSurfaceHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) - * touchScreenHeight / mSurfaceHeight + touchScreenTop; + virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) + * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft; + virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) + * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft; + virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) + * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; + virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) + * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; - LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", - virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); - } - } // release virtual key lock + LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", + virtualKey.scanCode, virtualKey.keyCode, + virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } } void TouchInputMapper::reset() { @@ -1329,20 +1379,16 @@ void TouchInputMapper::reset() { syncTouch(when, true); } - InputMapper::reset(); + { // acquire lock + AutoMutex _l(mLock); + initializeLocked(); + } // release lock - // Reinitialize. - initialize(); + InputMapper::reset(); } void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { - /* Refresh associated display information and update our size configuration if needed. */ - - if (! configureSurface()) { - return; - } - - /* Apply policy */ + // Apply generic policy actions. uint32_t policyFlags = 0; int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags); @@ -1352,7 +1398,7 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { return; // event dropped } - /* Preprocess pointer data */ + // Preprocess pointer data. if (mParameters.useBadTouchFilter) { if (applyBadTouchFilter()) { @@ -1381,14 +1427,14 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { savedTouch = & mCurrentTouch; } - /* Process touches and virtual keys */ + // Process touches and virtual keys. TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); if (touchResult == DISPATCH_TOUCH) { dispatchTouches(when, policyFlags); } - /* Copy current touch to last touch in preparation for the next cycle. */ + // Copy current touch to last touch in preparation for the next cycle. if (touchResult == DROP_STROKE) { mLastTouch.clear(); @@ -1403,13 +1449,19 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( int32_t keyCode, scanCode, downTime; TouchResult touchResult; - { // acquire virtual key lock - AutoMutex _l(mVirtualKeyLock); + { // acquire lock + AutoMutex _l(mLock); + + // Update surface size and orientation, including virtual key positions. + if (! configureSurfaceLocked()) { + return DROP_STROKE; + } - if (mCurrentVirtualKey.down) { + // Check for virtual key press. + if (mLocked.currentVirtualKey.down) { if (mCurrentTouch.pointerCount == 0) { // Pointer went up while virtual key was down. - mCurrentVirtualKey.down = false; + mLocked.currentVirtualKey.down = false; #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); @@ -1423,8 +1475,8 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( if (mCurrentTouch.pointerCount == 1) { int32_t x = mCurrentTouch.pointers[0].x; int32_t y = mCurrentTouch.pointers[0].y; - const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y); - if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y); + if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.keyCode) { // Pointer is still within the space of the virtual key. return SKIP_TOUCH; } @@ -1434,7 +1486,7 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( // Send key cancellation and drop the stroke so subsequent motions will be // considered fresh downs. This is useful when the user swipes away from the // virtual key area into the main display surface. - mCurrentVirtualKey.down = false; + mLocked.currentVirtualKey.down = false; #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); @@ -1449,16 +1501,16 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( // Pointer just went down. Handle off-screen touches, if needed. int32_t x = mCurrentTouch.pointers[0].x; int32_t y = mCurrentTouch.pointers[0].y; - if (! isPointInsideSurface(x, y)) { + if (! isPointInsideSurfaceLocked(x, y)) { // If exactly one pointer went down, check for virtual key hit. // Otherwise we will drop the entire stroke. if (mCurrentTouch.pointerCount == 1) { - const VirtualKey* virtualKey = findVirtualKeyHitLvk(x, y); + const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y); if (virtualKey) { - mCurrentVirtualKey.down = true; - mCurrentVirtualKey.downTime = when; - mCurrentVirtualKey.keyCode = virtualKey->keyCode; - mCurrentVirtualKey.scanCode = virtualKey->scanCode; + mLocked.currentVirtualKey.down = true; + mLocked.currentVirtualKey.downTime = when; + mLocked.currentVirtualKey.keyCode = virtualKey->keyCode; + mLocked.currentVirtualKey.scanCode = virtualKey->scanCode; #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); @@ -1478,12 +1530,20 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( DispatchVirtualKey: // Collect remaining state needed to dispatch virtual key. - keyCode = mCurrentVirtualKey.keyCode; - scanCode = mCurrentVirtualKey.scanCode; - downTime = mCurrentVirtualKey.downTime; - } // release virtual key lock + keyCode = mLocked.currentVirtualKey.keyCode; + scanCode = mLocked.currentVirtualKey.scanCode; + downTime = mLocked.currentVirtualKey.downTime; + } // release lock // Dispatch virtual key. + applyPolicyAndDispatchVirtualKey(when, policyFlags, keyEventAction, keyEventFlags, + keyCode, scanCode, downTime); + return touchResult; +} + +void TouchInputMapper::applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags, + int32_t keyCode, int32_t scanCode, nsecs_t downTime) { int32_t metaState = mContext->getGlobalMetaState(); if (keyEventAction == AKEY_EVENT_ACTION_DOWN) { @@ -1497,7 +1557,6 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); } - return touchResult; } void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { @@ -1566,107 +1625,118 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, uint32_t pointerCount = 0; int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; + int32_t motionEventEdgeFlags = 0; + float xPrecision, yPrecision; + + { // acquire lock + AutoMutex _l(mLock); + + // Walk through the the active pointers and map touch screen coordinates (TouchData) into + // display coordinates (PointerCoords) and adjust for display orientation. + while (! idBits.isEmpty()) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + uint32_t index = touch->idToIndex[id]; + + float x = float(touch->pointers[index].x - mLocked.xOrigin) * mLocked.xScale; + float y = float(touch->pointers[index].y - mLocked.yOrigin) * mLocked.yScale; + float pressure = float(touch->pointers[index].pressure - mLocked.pressureOrigin) + * mLocked.pressureScale; + float size = float(touch->pointers[index].size - mLocked.sizeOrigin) + * mLocked.sizeScale; + + float orientation = float(touch->pointers[index].orientation) + * mLocked.orientationScale; + + float touchMajor, touchMinor, toolMajor, toolMinor; + if (abs(orientation) <= M_PI_4) { + // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X. + touchMajor = float(touch->pointers[index].touchMajor) * mLocked.yScale; + touchMinor = float(touch->pointers[index].touchMinor) * mLocked.xScale; + toolMajor = float(touch->pointers[index].toolMajor) * mLocked.yScale; + toolMinor = float(touch->pointers[index].toolMinor) * mLocked.xScale; + } else { + // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y. + touchMajor = float(touch->pointers[index].touchMajor) * mLocked.xScale; + touchMinor = float(touch->pointers[index].touchMinor) * mLocked.yScale; + toolMajor = float(touch->pointers[index].toolMajor) * mLocked.xScale; + toolMinor = float(touch->pointers[index].toolMinor) * mLocked.yScale; + } - // Walk through the the active pointers and map touch screen coordinates (TouchData) into - // display coordinates (PointerCoords) and adjust for display orientation. - while (! idBits.isEmpty()) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - uint32_t index = touch->idToIndex[id]; - - float x = float(touch->pointers[index].x - mXOrigin) * mXScale; - float y = float(touch->pointers[index].y - mYOrigin) * mYScale; - float pressure = float(touch->pointers[index].pressure - mPressureOrigin) * mPressureScale; - float size = float(touch->pointers[index].size - mSizeOrigin) * mSizeScale; - - float orientation = float(touch->pointers[index].orientation) * mOrientationScale; - - float touchMajor, touchMinor, toolMajor, toolMinor; - if (abs(orientation) <= M_PI_4) { - // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X. - touchMajor = float(touch->pointers[index].touchMajor) * mYScale; - touchMinor = float(touch->pointers[index].touchMinor) * mXScale; - toolMajor = float(touch->pointers[index].toolMajor) * mYScale; - toolMinor = float(touch->pointers[index].toolMinor) * mXScale; - } else { - // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y. - touchMajor = float(touch->pointers[index].touchMajor) * mXScale; - touchMinor = float(touch->pointers[index].touchMinor) * mYScale; - toolMajor = float(touch->pointers[index].toolMajor) * mXScale; - toolMinor = float(touch->pointers[index].toolMinor) * mYScale; - } - - switch (mSurfaceOrientation) { - case InputReaderPolicyInterface::ROTATION_90: { - float xTemp = x; - x = y; - y = mSurfaceWidth - xTemp; - orientation -= M_PI_2; - if (orientation < - M_PI_2) { - orientation += M_PI; + switch (mLocked.surfaceOrientation) { + case InputReaderPolicyInterface::ROTATION_90: { + float xTemp = x; + x = y; + y = mLocked.surfaceWidth - xTemp; + orientation -= M_PI_2; + if (orientation < - M_PI_2) { + orientation += M_PI; + } + break; + } + case InputReaderPolicyInterface::ROTATION_180: { + x = mLocked.surfaceWidth - x; + y = mLocked.surfaceHeight - y; + orientation = - orientation; + break; + } + case InputReaderPolicyInterface::ROTATION_270: { + float xTemp = x; + x = mLocked.surfaceHeight - y; + y = xTemp; + orientation += M_PI_2; + if (orientation > M_PI_2) { + orientation -= M_PI; + } + break; } - break; - } - case InputReaderPolicyInterface::ROTATION_180: { - x = mSurfaceWidth - x; - y = mSurfaceHeight - y; - orientation = - orientation; - break; - } - case InputReaderPolicyInterface::ROTATION_270: { - float xTemp = x; - x = mSurfaceHeight - y; - y = xTemp; - orientation += M_PI_2; - if (orientation > M_PI_2) { - orientation -= M_PI; } - break; - } - } - - pointerIds[pointerCount] = int32_t(id); - pointerCoords[pointerCount].x = x; - pointerCoords[pointerCount].y = y; - pointerCoords[pointerCount].pressure = pressure; - pointerCoords[pointerCount].size = size; - pointerCoords[pointerCount].touchMajor = touchMajor; - pointerCoords[pointerCount].touchMinor = touchMinor; - pointerCoords[pointerCount].toolMajor = toolMajor; - pointerCoords[pointerCount].toolMinor = toolMinor; - pointerCoords[pointerCount].orientation = orientation; + pointerIds[pointerCount] = int32_t(id); - if (id == changedId) { - motionEventAction |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } + pointerCoords[pointerCount].x = x; + pointerCoords[pointerCount].y = y; + pointerCoords[pointerCount].pressure = pressure; + pointerCoords[pointerCount].size = size; + pointerCoords[pointerCount].touchMajor = touchMajor; + pointerCoords[pointerCount].touchMinor = touchMinor; + pointerCoords[pointerCount].toolMajor = toolMajor; + pointerCoords[pointerCount].toolMinor = toolMinor; + pointerCoords[pointerCount].orientation = orientation; - pointerCount += 1; - } + if (id == changedId) { + motionEventAction |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } - // Check edge flags by looking only at the first pointer since the flags are - // global to the event. - int32_t motionEventEdgeFlags = 0; - if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - if (pointerCoords[0].x <= 0) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (pointerCoords[0].x >= mOrientedSurfaceWidth) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; + pointerCount += 1; } - if (pointerCoords[0].y <= 0) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (pointerCoords[0].y >= mOrientedSurfaceHeight) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; + + // Check edge flags by looking only at the first pointer since the flags are + // global to the event. + if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { + if (pointerCoords[0].x <= 0) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; + } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; + } + if (pointerCoords[0].y <= 0) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; + } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) { + motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; + } } - } + + xPrecision = mLocked.orientedXPrecision; + yPrecision = mLocked.orientedYPrecision; + } // release lock getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags, motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags, pointerCount, pointerIds, pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); + xPrecision, yPrecision, mDownTime); } -bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { +bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { if (mAxes.x.valid && mAxes.y.valid) { return x >= mAxes.x.minValue && x <= mAxes.x.maxValue && y >= mAxes.y.minValue && y <= mAxes.y.maxValue; @@ -1674,9 +1744,11 @@ bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { return true; } -const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLvk(int32_t x, int32_t y) { - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked( + int32_t x, int32_t y) { + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " @@ -2224,50 +2296,53 @@ void TouchInputMapper::applyAveragingTouchFilter() { } int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - { // acquire virtual key lock - AutoMutex _l(mVirtualKeyLock); + { // acquire lock + AutoMutex _l(mLock); - if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) { return AKEY_STATE_VIRTUAL; } - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; if (virtualKey.keyCode == keyCode) { return AKEY_STATE_UP; } } - } // release virtual key lock + } // release lock return AKEY_STATE_UNKNOWN; } int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - { // acquire virtual key lock - AutoMutex _l(mVirtualKeyLock); + { // acquire lock + AutoMutex _l(mLock); - if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) { return AKEY_STATE_VIRTUAL; } - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; if (virtualKey.scanCode == scanCode) { return AKEY_STATE_UP; } } - } // release virtual key lock + } // release lock return AKEY_STATE_UNKNOWN; } bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - { // acquire virtual key lock - AutoMutex _l(mVirtualKeyLock); + { // acquire lock + AutoMutex _l(mLock); - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; + size_t numVirtualKeys = mLocked.virtualKeys.size(); + for (size_t i = 0; i < numVirtualKeys; i++) { + const VirtualKey& virtualKey = mLocked.virtualKeys[i]; for (size_t i = 0; i < numCodes; i++) { if (virtualKey.keyCode == keyCodes[i]) { @@ -2275,7 +2350,7 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode } } } - } // release virtual key lock + } // release lock return true; } @@ -2304,7 +2379,6 @@ void SingleTouchInputMapper::initialize() { void SingleTouchInputMapper::reset() { TouchInputMapper::reset(); - // Reinitialize. initialize(); } @@ -2436,7 +2510,6 @@ void MultiTouchInputMapper::initialize() { void MultiTouchInputMapper::reset() { TouchInputMapper::reset(); - // Reinitialize. initialize(); } diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index f26676d..c40d285 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -344,7 +344,7 @@ status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { LOGV("setParamMaxFileDurationUs: %lld us", timeUs); - if (timeUs <= 1000000LL) { // XXX: 1 second + if (timeUs <= 100000LL) { // XXX: 100 milli-seconds LOGE("Max file duration is too short: %lld us", timeUs); return BAD_VALUE; } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index baf9f4f..9f712c3 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -1615,6 +1615,16 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->write(kData2, sizeof(kData2)); mOwner->endBox(); // esds + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) || + !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { + // 3gpp2 Spec AMRSampleEntry fields + mOwner->beginBox("damr"); + mOwner->writeCString(" "); // vendor: 4 bytes + mOwner->writeInt8(0); // decoder version + mOwner->writeInt16(0x83FF); // mode set: all enabled + mOwner->writeInt8(0); // mode change period + mOwner->writeInt8(1); // frames per sample + mOwner->endBox(); } mOwner->endBox(); } else { diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk index 8abd649..b5c018f 100644 --- a/opengl/libagl/Android.mk +++ b/opengl/libagl/Android.mk @@ -6,9 +6,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -# Set to 1 to use gralloc and copybits -LIBAGL_USE_GRALLOC_COPYBITS := 1 - LOCAL_SRC_FILES:= \ egl.cpp \ state.cpp \ @@ -51,13 +48,6 @@ ifneq ($(TARGET_SIMULATOR),true) LOCAL_C_INCLUDES += bionic/libc/private endif -ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1) - LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS - LOCAL_SRC_FILES += copybit.cpp - LOCAL_SHARED_LIBRARIES += libui -endif - - LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl LOCAL_MODULE:= libGLES_android diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp index 255ccac..bbb82fc 100644 --- a/opengl/libagl/TextureObjectManager.cpp +++ b/opengl/libagl/TextureObjectManager.cpp @@ -55,9 +55,6 @@ void EGLTextureObject::init() memset(crop_rect, 0, sizeof(crop_rect)); generate_mipmap = GL_FALSE; direct = GL_FALSE; -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - try_copybit = false; -#endif // LIBAGL_USE_GRALLOC_COPYBITS buffer = 0; } diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h index 279e040..70e3bef 100644 --- a/opengl/libagl/TextureObjectManager.h +++ b/opengl/libagl/TextureObjectManager.h @@ -80,9 +80,6 @@ public: GLint crop_rect[4]; GLint generate_mipmap; GLint direct; -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - bool try_copybit; -#endif // LIBAGL_USE_GRALLOC_COPYBITS android_native_buffer_t* buffer; }; diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp index 71825c5..4997dc8 100644 --- a/opengl/libagl/array.cpp +++ b/opengl/libagl/array.cpp @@ -26,9 +26,6 @@ #include "primitives.h" #include "texture.h" #include "BufferObjectManager.h" -#ifdef LIBAGL_USE_GRALLOC_COPYBITS -#include "copybit.h" -#endif // LIBAGL_USE_GRALLOC_COPYBITS // ---------------------------------------------------------------------------- @@ -707,12 +704,6 @@ void drawPrimitivesTriangleStrip(ogles_context_t* c, void drawPrimitivesTriangleFan(ogles_context_t* c, GLint first, GLsizei count) { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (drawTriangleFanWithCopybit(c, first, count)) { - return; - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS - drawPrimitivesTriangleFanOrStrip(c, first, count, 2); } diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp deleted file mode 100644 index 67d1ce7..0000000 --- a/opengl/libagl/copybit.cpp +++ /dev/null @@ -1,618 +0,0 @@ -/* -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdlib.h> -#include <stdio.h> - -#include "context.h" -#include "fp.h" -#include "state.h" -#include "matrix.h" -#include "vertex.h" -#include "light.h" -#include "primitives.h" -#include "texture.h" -#include "BufferObjectManager.h" -#include "TextureObjectManager.h" - -#include <hardware/gralloc.h> -#include <hardware/copybit.h> -#include <private/ui/android_natives_priv.h> - -#include <ui/GraphicBuffer.h> -#include <ui/Region.h> -#include <ui/Rect.h> - - -#define DEBUG_COPYBIT false - -// ---------------------------------------------------------------------------- - -namespace android { - -static void textureToCopyBitImage( - const GGLSurface* surface, int32_t opFormat, - android_native_buffer_t* buffer, copybit_image_t* img) -{ - img->w = surface->stride; - img->h = surface->height; - img->format = opFormat; - img->base = surface->data; - img->handle = (native_handle_t *)buffer->handle; -} - -struct clipRectRegion : public copybit_region_t { - clipRectRegion(ogles_context_t* c) - { - scissor_t const* scissor = &c->rasterizer.state.scissor; - r.l = scissor->left; - r.t = scissor->top; - r.r = scissor->right; - r.b = scissor->bottom; - next = iterate; - } -private: - static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { - *rect = static_cast<clipRectRegion const*>(self)->r; - const_cast<copybit_region_t *>(self)->next = iterate_done; - return 1; - } - static int iterate_done(copybit_region_t const *, copybit_rect_t*) { - return 0; - } -public: - copybit_rect_t r; -}; - -static bool supportedCopybitsFormat(int format) { - switch (format) { - case COPYBIT_FORMAT_RGBA_8888: - case COPYBIT_FORMAT_RGBX_8888: - case COPYBIT_FORMAT_RGB_888: - case COPYBIT_FORMAT_RGB_565: - case COPYBIT_FORMAT_BGRA_8888: - case COPYBIT_FORMAT_RGBA_5551: - case COPYBIT_FORMAT_RGBA_4444: - return true; - default: - return false; - } -} - -static bool hasAlpha(int format) { - switch (format) { - case COPYBIT_FORMAT_RGBA_8888: - case COPYBIT_FORMAT_BGRA_8888: - case COPYBIT_FORMAT_RGBA_5551: - case COPYBIT_FORMAT_RGBA_4444: - return true; - default: - return false; - } -} - -static inline int fixedToByte(GGLfixed val) { - return (val - (val >> 8)) >> 8; -} - -/** - * Performs a quick check of the rendering state. If this function returns - * false we cannot use the copybit driver. - */ - -static bool checkContext(ogles_context_t* c) { - - // By convention copybitQuickCheckContext() has already returned true. - // avoid checking the same information again. - - if (c->copybits.blitEngine == NULL) { - LOGD_IF(DEBUG_COPYBIT, "no copybit hal"); - return false; - } - - if (c->rasterizer.state.enables - & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) { - LOGD_IF(DEBUG_COPYBIT, "depth test and/or fog"); - return false; - } - - // Note: The drawSurfaceBuffer is only set for destination - // surfaces types that are supported by the hardware and - // do not have an alpha channel. So we don't have to re-check that here. - - static const int tmu = 0; - texture_unit_t& u(c->textures.tmu[tmu]); - EGLTextureObject* textureObject = u.texture; - - if (!supportedCopybitsFormat(textureObject->surface.format)) { - LOGD_IF(DEBUG_COPYBIT, "texture format not supported"); - return false; - } - return true; -} - - -static bool copybit(GLint x, GLint y, - GLint w, GLint h, - EGLTextureObject* textureObject, - const GLint* crop_rect, - int transform, - ogles_context_t* c) -{ - status_t err = NO_ERROR; - - // We assume checkContext has already been called and has already - // returned true. - - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - - y = cbSurface.height - (y + h); - - const GLint Ucr = crop_rect[0]; - const GLint Vcr = crop_rect[1]; - const GLint Wcr = crop_rect[2]; - const GLint Hcr = crop_rect[3]; - - GLint screen_w = w; - GLint screen_h = h; - int32_t dsdx = Wcr << 16; // dsdx = ((Wcr/screen_w)/Wt)*Wt - int32_t dtdy = Hcr << 16; // dtdy = -((Hcr/screen_h)/Ht)*Ht - if (transform & COPYBIT_TRANSFORM_ROT_90) { - swap(screen_w, screen_h); - } - if (dsdx!=screen_w || dtdy!=screen_h) { - // in most cases the divide is not needed - dsdx /= screen_w; - dtdy /= screen_h; - } - dtdy = -dtdy; // see equation of dtdy above - - // copybit doesn't say anything about filtering, so we can't - // discriminate. On msm7k, copybit will always filter. - // the code below handles min/mag filters, we keep it as a reference. - -#ifdef MIN_MAG_FILTER - int32_t texelArea = gglMulx(dtdy, dsdx); - if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) { - // Non-linear filtering on a texture enlargement. - LOGD_IF(DEBUG_COPYBIT, "mag filter is not GL_LINEAR"); - return false; - } - if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) { - // Non-linear filtering on an texture shrink. - LOGD_IF(DEBUG_COPYBIT, "min filter is not GL_LINEAR"); - return false; - } -#endif - - const uint32_t enables = c->rasterizer.state.enables; - int planeAlpha = 255; - bool alphaPlaneWorkaround = false; - static const int tmu = 0; - texture_t& tev(c->rasterizer.state.texture[tmu]); - int32_t opFormat = textureObject->surface.format; - const bool srcTextureHasAlpha = hasAlpha(opFormat); - if (!srcTextureHasAlpha) { - planeAlpha = fixedToByte(c->currentColorClamped.a); - } - - const bool cbHasAlpha = hasAlpha(cbSurface.format); - bool blending = false; - if ((enables & GGL_ENABLE_BLENDING) - && !(c->rasterizer.state.blend.src == GL_ONE - && c->rasterizer.state.blend.dst == GL_ZERO)) { - // Blending is OK if it is - // the exact kind of blending that the copybits hardware supports. - // Note: The hardware only supports - // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA, - // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA. - // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case, - // because the performance is worth it, even if the results are - // not correct. - if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA - || c->rasterizer.state.blend.src == GL_ONE) - && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA - && c->rasterizer.state.blend.alpha_separate == 0)) { - // Incompatible blend mode. - LOGD_IF(DEBUG_COPYBIT, "incompatible blend mode"); - return false; - } - blending = true; - } else { - if (cbHasAlpha) { - // NOTE: the result will be slightly wrong in this case because - // the destination alpha channel will be set to 1.0 instead of - // the iterated alpha value. *shrug*. - } - // disable plane blending and src blending for supported formats - planeAlpha = 255; - if (opFormat == COPYBIT_FORMAT_RGBA_8888) { - opFormat = COPYBIT_FORMAT_RGBX_8888; - } else { - if (srcTextureHasAlpha) { - LOGD_IF(DEBUG_COPYBIT, "texture format requires blending"); - return false; - } - } - } - - switch (tev.env) { - case GGL_REPLACE: - break; - case GGL_MODULATE: - // only cases allowed is: - // RGB source, color={1,1,1,a} -> can be done with GL_REPLACE - // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE - if (blending) { - if (c->currentColorClamped.r == c->currentColorClamped.a && - c->currentColorClamped.g == c->currentColorClamped.a && - c->currentColorClamped.b == c->currentColorClamped.a) { - // TODO: RGBA source, color={1,1,1,a} / regular-blending - // is equivalent - alphaPlaneWorkaround = true; - break; - } - } - LOGD_IF(DEBUG_COPYBIT, "GGL_MODULATE"); - return false; - default: - // Incompatible texture environment. - LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment"); - return false; - } - - copybit_device_t* copybit = c->copybits.blitEngine; - copybit_image_t src; - textureToCopyBitImage(&textureObject->surface, opFormat, - textureObject->buffer, &src); - copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr }; - - /* - * Below we perform extra passes needed to emulate things the h/w - * cannot do. - */ - - const GLfixed minScaleInv = gglDivQ(0x10000, c->copybits.minScale, 16); - const GLfixed maxScaleInv = gglDivQ(0x10000, c->copybits.maxScale, 16); - - sp<GraphicBuffer> tempBitmap; - - if (dsdx < maxScaleInv || dsdx > minScaleInv || - dtdy < maxScaleInv || dtdy > minScaleInv) - { - // The requested scale is out of the range the hardware - // can support. - LOGD_IF(DEBUG_COPYBIT, - "scale out of range dsdx=%08x (Wcr=%d / w=%d), " - "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d", - dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr); - - int32_t xscale=0x10000, yscale=0x10000; - if (dsdx > minScaleInv) xscale = c->copybits.minScale; - else if (dsdx < maxScaleInv) xscale = c->copybits.maxScale; - if (dtdy > minScaleInv) yscale = c->copybits.minScale; - else if (dtdy < maxScaleInv) yscale = c->copybits.maxScale; - dsdx = gglMulx(dsdx, xscale); - dtdy = gglMulx(dtdy, yscale); - - /* we handle only one step of resizing below. Handling an arbitrary - * number is relatively easy (replace "if" above by "while"), but requires - * two intermediate buffers and so far we never had the need. - */ - - if (dsdx < maxScaleInv || dsdx > minScaleInv || - dtdy < maxScaleInv || dtdy > minScaleInv) { - LOGD_IF(DEBUG_COPYBIT, - "scale out of range dsdx=%08x (Wcr=%d / w=%d), " - "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d", - dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr); - return false; - } - - const int tmp_w = gglMulx(srect.r - srect.l, xscale, 16); - const int tmp_h = gglMulx(srect.b - srect.t, yscale, 16); - - LOGD_IF(DEBUG_COPYBIT, - "xscale=%08x, yscale=%08x, dsdx=%08x, dtdy=%08x, tmp_w=%d, tmp_h=%d", - xscale, yscale, dsdx, dtdy, tmp_w, tmp_h); - - tempBitmap = new GraphicBuffer( - tmp_w, tmp_h, src.format, - GraphicBuffer::USAGE_HW_2D); - - err = tempBitmap->initCheck(); - if (err == NO_ERROR) { - copybit_image_t tmp_dst; - copybit_rect_t tmp_rect; - tmp_dst.w = tmp_w; - tmp_dst.h = tmp_h; - tmp_dst.format = tempBitmap->format; - tmp_dst.handle = (native_handle_t*)tempBitmap->getNativeBuffer()->handle; - tmp_rect.l = 0; - tmp_rect.t = 0; - tmp_rect.r = tmp_dst.w; - tmp_rect.b = tmp_dst.h; - region_iterator tmp_it(Region(Rect(tmp_rect.r, tmp_rect.b))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - err = copybit->stretch(copybit, - &tmp_dst, &src, &tmp_rect, &srect, &tmp_it); - src = tmp_dst; - srect = tmp_rect; - } - } - - copybit_image_t dst; - textureToCopyBitImage(&cbSurface, cbSurface.format, - c->copybits.drawSurfaceBuffer, &dst); - copybit_rect_t drect = {x, y, x+w, y+h}; - - - /* and now the alpha-plane hack. This handles the "Fade" case of a - * texture with an alpha channel. - */ - if (alphaPlaneWorkaround) { - sp<GraphicBuffer> tempCb = new GraphicBuffer( - w, h, COPYBIT_FORMAT_RGB_565, - GraphicBuffer::USAGE_HW_2D); - - err = tempCb->initCheck(); - - copybit_image_t tmpCbImg; - copybit_rect_t tmpCbRect; - copybit_rect_t tmpdrect = drect; - tmpCbImg.w = w; - tmpCbImg.h = h; - tmpCbImg.format = tempCb->format; - tmpCbImg.handle = (native_handle_t*)tempCb->getNativeBuffer()->handle; - tmpCbRect.l = 0; - tmpCbRect.t = 0; - - if (drect.l < 0) { - tmpCbRect.l = -tmpdrect.l; - tmpdrect.l = 0; - } - if (drect.t < 0) { - tmpCbRect.t = -tmpdrect.t; - tmpdrect.t = 0; - } - if (drect.l + tmpCbImg.w > dst.w) { - tmpCbImg.w = dst.w - drect.l; - tmpdrect.r = dst.w; - } - if (drect.t + tmpCbImg.h > dst.h) { - tmpCbImg.h = dst.h - drect.t; - tmpdrect.b = dst.h; - } - - tmpCbRect.r = tmpCbImg.w; - tmpCbRect.b = tmpCbImg.h; - - if (!err) { - // first make a copy of the destination buffer - region_iterator tmp_it(Region(Rect(w, h))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - err = copybit->stretch(copybit, - &tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it); - } - if (!err) { - // then proceed as usual, but without the alpha plane - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, - (enables & GGL_ENABLE_DITHER) ? - COPYBIT_ENABLE : COPYBIT_DISABLE); - clipRectRegion it(c); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); - } - if (!err) { - // finally copy back the destination on top with 1-alphaplane - int invPlaneAlpha = 0xFF - fixedToByte(c->currentColorClamped.a); - clipRectRegion it(c); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, invPlaneAlpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - err = copybit->stretch(copybit, - &dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it); - } - } else { - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, - (enables & GGL_ENABLE_DITHER) ? - COPYBIT_ENABLE : COPYBIT_DISABLE); - clipRectRegion it(c); - - LOGD_IF(0, - "dst={%d, %d, %d, %p, %p}, " - "src={%d, %d, %d, %p, %p}, " - "drect={%d,%d,%d,%d}, " - "srect={%d,%d,%d,%d}, " - "it={%d,%d,%d,%d}, " , - dst.w, dst.h, dst.format, dst.base, dst.handle, - src.w, src.h, src.format, src.base, src.handle, - drect.l, drect.t, drect.r, drect.b, - srect.l, srect.t, srect.r, srect.b, - it.r.l, it.r.t, it.r.r, it.r.b - ); - - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); - } - if (err != NO_ERROR) { - c->textures.tmu[0].texture->try_copybit = false; - } - return err == NO_ERROR ? true : false; -} - -/* - * Try to draw a triangle fan with copybit, return false if we fail. - */ -bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count) -{ - if (!checkContext(c)) { - return false; - } - - // FIXME: we should handle culling here - c->arrays.compileElements(c, c->vc.vBuffer, 0, 4); - - // we detect if we're dealing with a rectangle, by comparing the - // rectangles {v0,v2} and {v1,v3} which should be identical. - - // NOTE: we should check that the rectangle is window aligned, however - // if we do that, the optimization won't be taken in a lot of cases. - // Since this code is intended to be used with SurfaceFlinger only, - // so it's okay... - - const vec4_t& v0 = c->vc.vBuffer[0].window; - const vec4_t& v1 = c->vc.vBuffer[1].window; - const vec4_t& v2 = c->vc.vBuffer[2].window; - const vec4_t& v3 = c->vc.vBuffer[3].window; - int l = min(v0.x, v2.x); - int b = min(v0.y, v2.y); - int r = max(v0.x, v2.x); - int t = max(v0.y, v2.y); - if ((l != min(v1.x, v3.x)) || (b != min(v1.y, v3.y)) || - (r != max(v1.x, v3.x)) || (t != max(v1.y, v3.y))) { - LOGD_IF(DEBUG_COPYBIT, "geometry not a rectangle"); - return false; - } - - // fetch and transform texture coordinates - // NOTE: maybe it would be better to have a "compileElementsAll" method - // that would ensure all vertex data are fetched and transformed - const transform_t& tr = c->transforms.texture[0].transform; - for (size_t i=0 ; i<4 ; i++) { - const GLubyte* tp = c->arrays.texture[0].element(i); - vertex_t* const v = &c->vc.vBuffer[i]; - c->arrays.texture[0].fetch(c, v->texture[0].v, tp); - // FIXME: we should bail if q!=1 - c->arrays.tex_transform[0](&tr, &v->texture[0], &v->texture[0]); - } - - const vec4_t& t0 = c->vc.vBuffer[0].texture[0]; - const vec4_t& t1 = c->vc.vBuffer[1].texture[0]; - const vec4_t& t2 = c->vc.vBuffer[2].texture[0]; - const vec4_t& t3 = c->vc.vBuffer[3].texture[0]; - int txl = min(t0.x, t2.x); - int txb = min(t0.y, t2.y); - int txr = max(t0.x, t2.x); - int txt = max(t0.y, t2.y); - if ((txl != min(t1.x, t3.x)) || (txb != min(t1.y, t3.y)) || - (txr != max(t1.x, t3.x)) || (txt != max(t1.y, t3.y))) { - LOGD_IF(DEBUG_COPYBIT, "texcoord not a rectangle"); - return false; - } - if ((txl != 0) || (txb != 0) || - (txr != FIXED_ONE) || (txt != FIXED_ONE)) { - // we could probably handle this case, if we wanted to - LOGD_IF(DEBUG_COPYBIT, "texture is cropped: %08x,%08x,%08x,%08x", - txl, txb, txr, txt); - return false; - } - - // at this point, we know we are dealing with a rectangle, so we - // only need to consider 3 vertices for computing the jacobians - - const int dx01 = v1.x - v0.x; - const int dx02 = v2.x - v0.x; - const int dy01 = v1.y - v0.y; - const int dy02 = v2.y - v0.y; - const int ds01 = t1.S - t0.S; - const int ds02 = t2.S - t0.S; - const int dt01 = t1.T - t0.T; - const int dt02 = t2.T - t0.T; - const int area = dx01*dy02 - dy01*dx02; - int dsdx, dsdy, dtdx, dtdy; - if (area >= 0) { - dsdx = ds01*dy02 - ds02*dy01; - dtdx = dt01*dy02 - dt02*dy01; - dsdy = ds02*dx01 - ds01*dx02; - dtdy = dt02*dx01 - dt01*dx02; - } else { - dsdx = ds02*dy01 - ds01*dy02; - dtdx = dt02*dy01 - dt01*dy02; - dsdy = ds01*dx02 - ds02*dx01; - dtdy = dt01*dx02 - dt02*dx01; - } - - // here we rely on the fact that we know the transform is - // a rigid-body transform AND that it can only rotate in 90 degrees - // increments - - int transform = 0; - if (dsdx == 0) { - // 90 deg rotation case - // [ 0 dtdx ] - // [ dsdx 0 ] - transform |= COPYBIT_TRANSFORM_ROT_90; - // FIXME: not sure if FLIP_H and FLIP_V shouldn't be inverted - if (dtdx > 0) - transform |= COPYBIT_TRANSFORM_FLIP_H; - if (dsdy < 0) - transform |= COPYBIT_TRANSFORM_FLIP_V; - } else { - // [ dsdx 0 ] - // [ 0 dtdy ] - if (dsdx < 0) - transform |= COPYBIT_TRANSFORM_FLIP_H; - if (dtdy < 0) - transform |= COPYBIT_TRANSFORM_FLIP_V; - } - - //LOGD("l=%d, b=%d, w=%d, h=%d, tr=%d", x, y, w, h, transform); - //LOGD("A=%f\tB=%f\nC=%f\tD=%f", - // dsdx/65536.0, dtdx/65536.0, dsdy/65536.0, dtdy/65536.0); - - int x = l >> 4; - int y = b >> 4; - int w = (r-l) >> 4; - int h = (t-b) >> 4; - texture_unit_t& u(c->textures.tmu[0]); - EGLTextureObject* textureObject = u.texture; - GLint tWidth = textureObject->surface.width; - GLint tHeight = textureObject->surface.height; - GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight}; - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - y = cbSurface.height - (y + h); - return copybit(x, y, w, h, textureObject, crop_rect, transform, c); -} - -/* - * Try to drawTexiOESWithCopybit, return false if we fail. - */ - -bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z, - GLint w, GLint h, ogles_context_t* c) -{ - // quickly process empty rects - if ((w|h) <= 0) { - return true; - } - if (!checkContext(c)) { - return false; - } - texture_unit_t& u(c->textures.tmu[0]); - EGLTextureObject* textureObject = u.texture; - return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c); -} - -} // namespace android - diff --git a/opengl/libagl/copybit.h b/opengl/libagl/copybit.h deleted file mode 100644 index b8b5afd..0000000 --- a/opengl/libagl/copybit.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -** -** Copyright 2009, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_COPYBIT_H -#define ANDROID_OPENGLES_COPYBIT_H - -#include <stdlib.h> - -#include <GLES/gl.h> - -#include "TextureObjectManager.h" -namespace android { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - -bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z, - GLint w, GLint h, ogles_context_t* c); - -bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, - GLsizei count); - -inline bool copybitQuickCheckContext(ogles_context_t* c) { - return c->copybits.drawSurfaceBuffer != 0 - && c->rasterizer.state.enabled_tmu == 1 - && c->textures.tmu[0].texture->try_copybit; -} - -/* - * Tries to draw a drawTexiOES using copybit hardware. - * Returns true if successful. - */ -inline bool drawTexiOESWithCopybit(GLint x, GLint y, GLint z, - GLint w, GLint h, ogles_context_t* c) { - if (!copybitQuickCheckContext(c)) { - return false; - } - - return drawTexiOESWithCopybit_impl(x, y, z, w, h, c); -} - -/* - * Tries to draw a triangle fan using copybit hardware. - * Returns true if successful. - */ -inline bool drawTriangleFanWithCopybit(ogles_context_t* c, GLint first, - GLsizei count) { - /* - * We are looking for the glDrawArrays call made by SurfaceFlinger. - */ - - if ((count!=4) || first || !copybitQuickCheckContext(c)) - return false; - - return drawTriangleFanWithCopybit_impl(c, first, count); -} - - -#endif // LIBAGL_USE_GRALLOC_COPYBITS - -} // namespace android - -#endif // ANDROID_OPENGLES_COPYBIT_H diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 54d7307..5bbe441 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -628,23 +628,6 @@ EGLClientBuffer egl_window_surface_v2_t::getRenderBuffer() const return buffer; } -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - -static bool supportedCopybitsDestinationFormat(int format) { - // Hardware supported - switch (format) { - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_RGBA_4444: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_BGRA_8888: - return true; - } - return false; -} -#endif - EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl) { GGLSurface buffer; @@ -658,18 +641,6 @@ EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl) if (depth.data != gl->rasterizer.state.buffers.depth.data) gl->rasterizer.procs.depthBuffer(gl, &depth); -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - gl->copybits.drawSurfaceBuffer = 0; - if (gl->copybits.blitEngine != NULL) { - if (supportedCopybitsDestinationFormat(buffer.format)) { - buffer_handle_t handle = this->buffer->handle; - if (handle != NULL) { - gl->copybits.drawSurfaceBuffer = this->buffer; - } - } - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS - return EGL_TRUE; } EGLBoolean egl_window_surface_v2_t::bindReadSurface(ogles_context_t* gl) diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp index 27bb545..a0f720a 100644 --- a/opengl/libagl/state.cpp +++ b/opengl/libagl/state.cpp @@ -28,10 +28,6 @@ #include "BufferObjectManager.h" #include "TextureObjectManager.h" -#ifdef LIBAGL_USE_GRALLOC_COPYBITS -#include <hardware/copybit.h> -#endif // LIBAGL_USE_GRALLOC_COPYBITS - namespace android { // ---------------------------------------------------------------------------- @@ -101,35 +97,6 @@ ogles_context_t *ogles_init(size_t extra) // OpenGL enables dithering by default c->rasterizer.procs.enable(c, GL_DITHER); - c->copybits.blitEngine = NULL; - c->copybits.minScale = 0; - c->copybits.maxScale = 0; - c->copybits.drawSurfaceBuffer = 0; - -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - hw_module_t const* module; - if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { - struct copybit_device_t* copyBits; - if (copybit_open(module, ©Bits) == 0) { - c->copybits.blitEngine = copyBits; - { - int minLim = copyBits->get(copyBits, - COPYBIT_MINIFICATION_LIMIT); - if (minLim != -EINVAL && minLim > 0) { - c->copybits.minScale = (1 << 16) / minLim; - } - } - { - int magLim = copyBits->get(copyBits, - COPYBIT_MAGNIFICATION_LIMIT); - if (magLim != -EINVAL && magLim > 0) { - c->copybits.maxScale = min(32*1024-1, magLim) << 16; - } - } - } - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS - return c; } @@ -144,11 +111,6 @@ void ogles_uninit(ogles_context_t* c) c->bufferObjectManager->decStrong(c); ggl_uninit_context(&(c->rasterizer)); free(c->rasterizer.base); -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (c->copybits.blitEngine != NULL) { - copybit_close((struct copybit_device_t*) c->copybits.blitEngine); - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS } void _ogles_error(ogles_context_t* c, GLenum error) diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index d67612e..eb96895 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -26,10 +26,6 @@ #include <private/ui/android_natives_priv.h> #include <ETC1/etc1.h> -#ifdef LIBAGL_USE_GRALLOC_COPYBITS -#include "copybit.h" -#endif // LIBAGL_USE_GRALLOC_COPYBITS - namespace android { // ---------------------------------------------------------------------------- @@ -763,17 +759,10 @@ static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, ogles_context_t* c) { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (drawTexiOESWithCopybit(gglFixedToIntRound(x), - gglFixedToIntRound(y), gglFixedToIntRound(z), - gglFixedToIntRound(w), gglFixedToIntRound(h), c)) { - return; - } -#else // quickly reject empty rects if ((w|h) <= 0) return; -#endif + drawTexxOESImp(x, y, z, w, h, c); } @@ -785,11 +774,6 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte // which is a lot faster. if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) { -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - if (drawTexiOESWithCopybit(x, y, z, w, h, c)) { - return; - } -#endif const int tmu = 0; texture_unit_t& u(c->textures.tmu[tmu]); EGLTextureObject* textureObject = u.texture; @@ -797,9 +781,7 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte const GLint Hcr = textureObject->crop_rect[3]; if ((w == Wcr) && (h == -Hcr)) { -#ifndef LIBAGL_USE_GRALLOC_COPYBITS if ((w|h) <= 0) return; // quickly reject empty rects -#endif if (u.dirty) { c->rasterizer.procs.activeTexture(c, tmu); @@ -1646,13 +1628,6 @@ void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) // bind it to the texture unit sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); tex->setImage(native_buffer); - -#ifdef LIBAGL_USE_GRALLOC_COPYBITS - tex->try_copybit = false; - if (c->copybits.blitEngine != NULL) { - tex->try_copybit = true; - } -#endif // LIBAGL_USE_GRALLOC_COPYBITS } void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 6eaf0cc..f1c6532 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -125,8 +125,6 @@ public class DefaultContainerService extends IntentService { metrics.setToDefaults(); PackageParser.PackageLite pkg = packageParser.parsePackageLite( archiveFilePath, 0); - ret.packageName = pkg.packageName; - ret.installLocation = pkg.installLocation; // Nuke the parser reference right away and force a gc packageParser = null; Runtime.getRuntime().gc(); @@ -136,6 +134,7 @@ public class DefaultContainerService extends IntentService { return ret; } ret.packageName = pkg.packageName; + ret.installLocation = pkg.installLocation; ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags); return ret; } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 75045d7..0fccbe7 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -7,6 +7,7 @@ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <application + android:persistent="true" android:allowClearUserData="false" android:label="@string/app_label" android:icon="@drawable/ic_launcher_settings"> diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index b29c6e6..ff4ff74 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -5019,7 +5019,12 @@ class PackageManagerService extends IPackageManager.Stub { @Override void handleReturnCode() { - processPendingInstall(mArgs, mRet); + // If mArgs is null, then MCS couldn't be reached. When it + // reconnects, it will try again to install. At that point, this + // will succeed. + if (mArgs != null) { + processPendingInstall(mArgs, mRet); + } } @Override diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 5615232..4940311 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5514,7 +5514,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException( "Injecting to another application requires INJECT_EVENTS permission"); case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED: - Slog.v(TAG, "Input event injection succeeded."); + //Slog.v(TAG, "Input event injection succeeded."); return true; case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT: Slog.w(TAG, "Input event injection timed out."); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index bcbda3e..162fffe 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -6101,17 +6101,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public void handleApplicationStrictModeViolation( - IBinder app, int violationMask, ApplicationErrorReport.CrashInfo crashInfo) { + IBinder app, + int violationMask, + StrictMode.ViolationInfo info) { ProcessRecord r = findAppProcess(app); if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) { - Integer stackFingerprint = crashInfo.stackTrace.hashCode(); + Integer stackFingerprint = info.crashInfo.stackTrace.hashCode(); boolean logIt = true; synchronized (mAlreadyLoggedViolatedStacks) { if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) { logIt = false; // TODO: sub-sample into EventLog for these, with - // the crashInfo.durationMillis? Then we'd get + // the info.durationMillis? Then we'd get // the relative pain numbers, without logging all // the stack traces repeatedly. We'd want to do // likewise in the client code, which also does @@ -6124,7 +6126,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } if (logIt) { - logStrictModeViolationToDropBox(r, crashInfo); + logStrictModeViolationToDropBox(r, info); } } @@ -6139,7 +6141,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen data.put("result", result); data.put("app", r); data.put("violationMask", violationMask); - data.put("crashInfo", crashInfo); + data.put("info", info); msg.obj = data; mHandler.sendMessage(msg); @@ -6154,9 +6156,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // these in quick succession so we try to batch these together to // minimize disk writes, number of dropbox entries, and maximize // compression, by having more fewer, larger records. - private void logStrictModeViolationToDropBox(ProcessRecord process, - ApplicationErrorReport.CrashInfo crashInfo) { - if (crashInfo == null) { + private void logStrictModeViolationToDropBox( + ProcessRecord process, + StrictMode.ViolationInfo info) { + if (info == null) { return; } final boolean isSystemApp = process == null || @@ -6177,12 +6180,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen appendDropBoxProcessHeaders(process, sb); sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); sb.append("System-App: ").append(isSystemApp).append("\n"); - if (crashInfo != null && crashInfo.durationMillis != -1) { - sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n"); + sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n"); + if (info.violationNumThisLoop != 0) { + sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n"); + } + if (info != null && info.durationMillis != -1) { + sb.append("Duration-Millis: ").append(info.durationMillis).append("\n"); } sb.append("\n"); - if (crashInfo != null && crashInfo.stackTrace != null) { - sb.append(crashInfo.stackTrace); + if (info.crashInfo != null && info.crashInfo.stackTrace != null) { + sb.append(info.crashInfo.stackTrace); } sb.append("\n"); diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index a60d2be..03194ff 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -135,9 +135,9 @@ public class PhoneNumberUtils } // TODO: We don't check for SecurityException here (requires - // READ_PHONE_STATE permission). + // CALL_PRIVILEGED permission). if (scheme.equals("voicemail")) { - return TelephonyManager.getDefault().getVoiceMailNumber(); + return TelephonyManager.getDefault().getCompleteVoiceMailNumber(); } if (context == null) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ab63017..aa916e0 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -665,6 +665,25 @@ public class TelephonyManager { } /** + * Returns the complete voice mail number. Return null if it is unavailable. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED} + * + * @hide + */ + public String getCompleteVoiceMailNumber() { + try { + return getSubscriberInfo().getCompleteVoiceMailNumber(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** * Returns the voice mail count. Return 0 if unavailable. * <p> * Requires Permission: diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java new file mode 100644 index 0000000..9822694 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -0,0 +1,983 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + + + +import android.content.Context; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.RegistrantList; +import android.telephony.PhoneStateListener; + + +import java.util.ArrayList; +import java.util.List; + +/** + * @hide + * + * CallManager class provides an abstract layer for PhoneApp to access + * and control calls. It implements Phone interface. + * + * CallManager provides call and connection control as well as + * channel capability. + * + * There are three categories of APIs CallManager provided + * + * 1. Call control and operation, such as dial() and hangup() + * 2. Channel capabilities, such as CanConference() + * 3. Register notification + * + * + */ +public final class CallManager { + + private static final int EVENT_DISCONNECT = 100; + private static final int EVENT_CALL_STATE_CHANGED = 101; + + + // Singleton instance + private static final CallManager INSTANCE = new CallManager(); + + // list of registered phones + private final ArrayList<Phone> mPhones; + + // list of supported ringing calls + private final ArrayList<Call> mRingingCalls; + + // list of supported background calls + private final ArrayList<Call> mBackgroundCalls; + + // list of supported foreground calls + private final ArrayList<Call> mForegroundCalls; + + // empty connection list + private final ArrayList<Connection> emptyConnections = new ArrayList<Connection>(); + + // default phone as the first phone registered + private Phone mDefaultPhone; + + // state registrants + protected final RegistrantList mPreciseCallStateRegistrants + = new RegistrantList(); + + protected final RegistrantList mNewRingingConnectionRegistrants + = new RegistrantList(); + + protected final RegistrantList mIncomingRingRegistrants + = new RegistrantList(); + + protected final RegistrantList mDisconnectRegistrants + = new RegistrantList(); + + protected final RegistrantList mServiceStateRegistrants + = new RegistrantList(); + + protected final RegistrantList mMmiCompleteRegistrants + = new RegistrantList(); + + protected final RegistrantList mMmiRegistrants + = new RegistrantList(); + + protected final RegistrantList mUnknownConnectionRegistrants + = new RegistrantList(); + + protected final RegistrantList mSuppServiceFailedRegistrants + = new RegistrantList(); + + private CallManager() { + mPhones = new ArrayList<Phone>(); + mRingingCalls = new ArrayList<Call>(); + mBackgroundCalls = new ArrayList<Call>(); + mForegroundCalls = new ArrayList<Call>(); + mDefaultPhone = null; + } + + /** + * get singleton instance of CallManager + * @return CallManager + */ + public static CallManager getInstance() { + return INSTANCE; + } + + /** + * Register phone to CallManager + * @param phone + * @return + */ + public boolean registerPhone(Phone phone) { + if (phone != null && !mPhones.contains(phone)) { + if (mPhones.isEmpty()) { + mDefaultPhone = phone; + } + mPhones.add(phone); + mRingingCalls.add(phone.getRingingCall()); + mBackgroundCalls.add(phone.getBackgroundCall()); + mForegroundCalls.add(phone.getForegroundCall()); + registerForPhoneStates(phone); + return true; + } + return false; + } + + /** + * unregister phone from CallManager + * @param phone + */ + public void unregisterPhone(Phone phone) { + if (phone != null && !mPhones.contains(phone)) { + mPhones.remove(phone); + mRingingCalls.remove(phone.getRingingCall()); + mBackgroundCalls.remove(phone.getBackgroundCall()); + mForegroundCalls.remove(phone.getForegroundCall()); + unregisterForPhoneStates(phone); + if (phone == mDefaultPhone) { + if (mPhones.isEmpty()) { + mDefaultPhone = null; + } else { + mDefaultPhone = mPhones.get(0); + } + } + } + } + + private void registerForPhoneStates(Phone phone) { + phone.registerForPreciseCallStateChanged(mHandler, EVENT_CALL_STATE_CHANGED, null); + phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null); + } + + private void unregisterForPhoneStates(Phone phone) { + phone.unregisterForPreciseCallStateChanged(mHandler); + phone.unregisterForDisconnect(mHandler); + } + + /** + * Answers a ringing or waiting call. + * + * Active call, if any, go on hold. + * If active call can't be held, i.e., a background call of the same channel exists, + * the active call will be hang up. + * + * Answering occurs asynchronously, and final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException when call is not ringing or waiting + */ + public void acceptCall(Call ringingCall) throws CallStateException { + Phone ringingPhone = ringingCall.getPhone(); + + if ( hasActiveFgCall() ) { + Phone activePhone = getActiveFgCall().getPhone(); + boolean hasBgCall = activePhone.getBackgroundCall().isIdle(); + boolean sameChannel = (activePhone == ringingPhone); + + if (sameChannel && hasBgCall) { + getActiveFgCall().hangup(); + } else if (!sameChannel && !hasBgCall) { + activePhone.switchHoldingAndActive(); + } else if (!sameChannel && hasBgCall) { + getActiveFgCall().hangup(); + } + } + + ringingPhone.acceptCall(); + } + + /** + * Reject (ignore) a ringing call. In GSM, this means UDUB + * (User Determined User Busy). Reject occurs asynchronously, + * and final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException when no call is ringing or waiting + */ + public void rejectCall(Call ringingCall) throws CallStateException { + Phone ringingPhone = ringingCall.getPhone(); + + ringingPhone.rejectCall(); + } + + /** + * Places any active calls on hold, and makes any held calls + * active. Switch occurs asynchronously and may fail. + * Final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException if active call is ringing, waiting, or + * dialing/alerting, or heldCall can�t be active. + * In these cases, this operation may not be performed. + */ + public void switchHoldingAndActive(Call heldCall) throws CallStateException { + Phone activePhone = null; + Phone heldPhone = null; + + if (hasActiveFgCall()) { + activePhone = getActiveFgCall().getPhone(); + } + + if (heldCall != null) { + heldPhone = heldCall.getPhone(); + } + + if (activePhone != heldPhone) { + activePhone.switchHoldingAndActive(); + } + + heldPhone.switchHoldingAndActive(); + } + + /** + * Whether or not the phone can conference in the current phone + * state--that is, one call holding and one call active. + * @return true if the phone can conference; false otherwise. + */ + public boolean canConference(Call heldCall) { + Phone activePhone = null; + Phone heldPhone = null; + + if (hasActiveFgCall()) { + activePhone = getActiveFgCall().getPhone(); + } + + if (heldCall != null) { + heldPhone = heldCall.getPhone(); + } + + return (heldPhone == activePhone); + } + + /** + * Conferences holding and active. Conference occurs asynchronously + * and may fail. Final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException if canConference() would return false. + * In these cases, this operation may not be performed. + */ + public void conference(Call heldCall) throws CallStateException { + if (canConference(heldCall)) + throw(new CallStateException("Can't conference foreground and selected background call")); + + heldCall.getPhone().conference(); + } + + /** + * Initiate a new voice connection. This happens asynchronously, so you + * cannot assume the audio path is connected (or a call index has been + * assigned) until PhoneStateChanged notification has occurred. + * + * @exception CallStateException if a new outgoing call is not currently + * possible because no more call slots exist or a call exists that is + * dialing, alerting, ringing, or waiting. Other errors are + * handled asynchronously. + */ + public Connection dial(Phone phone, String dialString) throws CallStateException { + return phone.dial(dialString); + } + + /** + * Initiate a new voice connection. This happens asynchronously, so you + * cannot assume the audio path is connected (or a call index has been + * assigned) until PhoneStateChanged notification has occurred. + * + * @exception CallStateException if a new outgoing call is not currently + * possible because no more call slots exist or a call exists that is + * dialing, alerting, ringing, or waiting. Other errors are + * handled asynchronously. + */ + public Connection dial(Phone phone, String dialString, UUSInfo uusInfo) throws CallStateException { + return phone.dial(dialString, uusInfo); + } + + /** + * clear disconnect connection for each phone + */ + public void clearDisconnected() { + for(Phone phone : mPhones) { + phone.clearDisconnected(); + } + } + + /** + * Whether or not the phone can do explicit call transfer in the current + * phone state--that is, one call holding and one call active. + * @return true if the phone can do explicit call transfer; false otherwise. + */ + public boolean canTransfer(Call heldCall) { + Phone activePhone = null; + Phone heldPhone = null; + + if (hasActiveFgCall()) { + activePhone = getActiveFgCall().getPhone(); + } + + if (heldCall != null) { + heldPhone = heldCall.getPhone(); + } + + return (heldPhone == activePhone && activePhone.canTransfer()); + } + + /** + * Connects the held call and active call + * Disconnects the subscriber from both calls + * + * Explicit Call Transfer occurs asynchronously + * and may fail. Final notification occurs via + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()}. + * + * @exception CallStateException if canTransfer() would return false. + * In these cases, this operation may not be performed. + */ + public void explicitCallTransfer(Call heldCall) throws CallStateException { + if (canTransfer(heldCall)) { + heldCall.getPhone().explicitCallTransfer(); + } + } + + /** + * Returns a list of MMI codes that are pending for a phone. (They have initiated + * but have not yet completed). + * Presently there is only ever one. + * + * Use <code>registerForMmiInitiate</code> + * and <code>registerForMmiComplete</code> for change notification. + * @return null if phone doesn't have or support mmi code + */ + public List<? extends MmiCode> getPendingMmiCodes(Phone phone) { + return null; + } + + /** + * Sends user response to a USSD REQUEST message. An MmiCode instance + * representing this response is sent to handlers registered with + * registerForMmiInitiate. + * + * @param ussdMessge Message to send in the response. + * @return false if phone doesn't support ussd service + */ + public boolean sendUssdResponse(Phone phone, String ussdMessge) { + return false; + } + + /** + * Mutes or unmutes the microphone for the active call. The microphone + * is automatically unmuted if a call is answered, dialed, or resumed + * from a holding state. + * + * @param muted true to mute the microphone, + * false to activate the microphone. + */ + + public void setMute(boolean muted) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().setMute(muted); + } + } + + /** + * Gets current mute status. Use + * {@link #registerForPreciseCallStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPreciseCallStateChanged()} + * as a change notifcation, although presently phone state changed is not + * fired when setMute() is called. + * + * @return true is muting, false is unmuting + */ + public boolean getMute() { + if (hasActiveFgCall()) { + return getActiveFgCall().getPhone().getMute(); + } + return false; + } + + /** + * Play a DTMF tone on the active call. + * + * @param c should be one of 0-9, '*' or '#'. Other values will be + * silently ignored. + * @return false if no active call or the active call doesn't support + * dtmf tone + */ + public boolean sendDtmf(char c) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().sendDtmf(c); + return true; + } + return false; + } + + /** + * Start to paly a DTMF tone on the active call. + * or there is a playing DTMF tone. + * @param c should be one of 0-9, '*' or '#'. Other values will be + * silently ignored. + * + * @return false if no active call or the active call doesn't support + * dtmf tone + */ + public boolean startDtmf(char c) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().sendDtmf(c); + return true; + } + return false; + } + + /** + * Stop the playing DTMF tone. Ignored if there is no playing DTMF + * tone or no active call. + */ + public void stopDtmf(Phone phone) { + phone.stopDtmf(); + } + + /** + * send burst DTMF tone, it can send the string as single character or multiple character + * ignore if there is no active call or not valid digits string. + * Valid digit means only includes characters ISO-LATIN characters 0-9, *, # + * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character, + * this api can send single character and multiple character, also, this api has response + * back to caller. + * + * @param dtmfString is string representing the dialing digit(s) in the active call + * @param on the DTMF ON length in milliseconds, or 0 for default + * @param off the DTMF OFF length in milliseconds, or 0 for default + * @param onComplete is the callback message when the action is processed by BP + * + */ + public boolean sendBurstDtmf(Phone phone, String dtmfString, int on, int off, Message onComplete) { + if (hasActiveFgCall()) { + getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete); + return true; + } + return false; + } + + /** + * Notifies when a voice connection has disconnected, either due to local + * or remote hangup or error. + * + * Messages received from this will have the following members:<p> + * <ul><li>Message.obj will be an AsyncResult</li> + * <li>AsyncResult.userObj = obj</li> + * <li>AsyncResult.result = a Connection object that is + * no longer connected.</li></ul> + */ + public void registerForDisconnect(Handler h, int what, Object obj) { + mDisconnectRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for voice disconnection notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForDisconnect(Handler h){ + mDisconnectRegistrants.remove(h); + } + + /** + * Register for getting notifications for change in the Call State {@link Call.State} + * This is called PreciseCallState because the call state is more precise than the + * {@link Phone.State} which can be obtained using the {@link PhoneStateListener} + * + * Resulting events will have an AsyncResult in <code>Message.obj</code>. + * AsyncResult.userData will be set to the obj argument here. + * The <em>h</em> parameter is held only by a weak reference. + */ + public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){ + mPreciseCallStateRegistrants.addUnique(h, what, obj); + } + + /** + * Unregisters for voice call state change notifications. + * Extraneous calls are tolerated silently. + */ + public void unregisterForPreciseCallStateChanged(Handler h){ + mPreciseCallStateRegistrants.remove(h); + } + + /** + * Notifies when a previously untracked non-ringing/waiting connection has appeared. + * This is likely due to some other entity (eg, SIM card application) initiating a call. + */ + public void registerForUnknownConnection(Handler h, int what, Object obj){} + + /** + * Unregisters for unknown connection notifications. + */ + public void unregisterForUnknownConnection(Handler h){} + + + /** + * Notifies when a new ringing or waiting connection has appeared.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = a Connection. <p> + * Please check Connection.isRinging() to make sure the Connection + * has not dropped since this message was posted. + * If Connection.isRinging() is true, then + * Connection.getCall() == Phone.getRingingCall() + */ + public void registerForNewRingingConnection(Handler h, int what, Object obj){} + + /** + * Unregisters for new ringing connection notification. + * Extraneous calls are tolerated silently + */ + + public void unregisterForNewRingingConnection(Handler h){} + + /** + * Notifies when an incoming call rings.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = a Connection. <p> + */ + public void registerForIncomingRing(Handler h, int what, Object obj){} + + /** + * Unregisters for ring notification. + * Extraneous calls are tolerated silently + */ + + public void unregisterForIncomingRing(Handler h){} + + /** + * Notifies when out-band ringback tone is needed.<p> + * + * Messages received from this: + * Message.obj will be an AsyncResult + * AsyncResult.userObj = obj + * AsyncResult.result = boolean, true to start play ringback tone + * and false to stop. <p> + */ + public void registerForRingbackTone(Handler h, int what, Object obj){} + + /** + * Unregisters for ringback tone notification. + */ + + public void unregisterForRingbackTone(Handler h){} + + /** + * Registers the handler to reset the uplink mute state to get + * uplink audio. + */ + public void registerForResendIncallMute(Handler h, int what, Object obj){} + + /** + * Unregisters for resend incall mute notifications. + */ + public void unregisterForResendIncallMute(Handler h){} + + + + /** + * Register for notifications of initiation of a new MMI code request. + * MMI codes for GSM are discussed in 3GPP TS 22.030.<p> + * + * Example: If Phone.dial is called with "*#31#", then the app will + * be notified here.<p> + * + * The returned <code>Message.obj</code> will contain an AsyncResult. + * + * <code>obj.result</code> will be an "MmiCode" object. + */ + public void registerForMmiInitiate(Handler h, int what, Object obj){} + + /** + * Unregisters for new MMI initiate notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForMmiInitiate(Handler h){} + + /** + * Register for notifications that an MMI request has completed + * its network activity and is in its final state. This may mean a state + * of COMPLETE, FAILED, or CANCELLED. + * + * <code>Message.obj</code> will contain an AsyncResult. + * <code>obj.result</code> will be an "MmiCode" object + */ + public void registerForMmiComplete(Handler h, int what, Object obj){} + + /** + * Unregisters for MMI complete notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForMmiComplete(Handler h){} + + /** + * Registration point for Ecm timer reset + * @param h handler to notify + * @param what user-defined message code + * @param obj placed in Message.obj + */ + public void registerForEcmTimerReset(Handler h, int what, Object obj){} + + /** + * Unregister for notification for Ecm timer reset + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForEcmTimerReset(Handler h){} + + + + /** + * Register for ServiceState changed. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a ServiceState instance + */ + public void registerForServiceStateChanged(Handler h, int what, Object obj){} + + /** + * Unregisters for ServiceStateChange notification. + * Extraneous calls are tolerated silently + */ + public void unregisterForServiceStateChanged(Handler h){} + + /** + * Register for Supplementary Service notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSuppServiceNotification(Handler h, int what, Object obj){} + + /** + * Unregisters for Supplementary Service notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSuppServiceNotification(Handler h){} + + /** + * Register for notifications when a supplementary service attempt fails. + * Message.obj will contain an AsyncResult. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForSuppServiceFailed(Handler h, int what, Object obj){} + + /** + * Unregister for notifications when a supplementary service attempt fails. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSuppServiceFailed(Handler h){} + + /** + * Register for notifications when a sInCall VoicePrivacy is enabled + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){} + + /** + * Unegister for notifications when a sInCall VoicePrivacy is enabled + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForInCallVoicePrivacyOn(Handler h){} + + /** + * Register for notifications when a sInCall VoicePrivacy is disabled + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){} + + /** + * Unegister for notifications when a sInCall VoicePrivacy is disabled + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForInCallVoicePrivacyOff(Handler h){} + + /** + * Register for notifications when CDMA OTA Provision status change + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){} + + /** + * Unegister for notifications when CDMA OTA Provision status change + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForCdmaOtaStatusChange(Handler h){} + + /** + * Registration point for subscription info ready + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){} + + /** + * Unregister for notifications for subscription info + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForSubscriptionInfoReady(Handler h){} + + /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls + * 1. APIs to access list of calls + * 2. APIs to check if any active call, which has connection other than + * disconnected ones, pleaser refer to Call.isIdle() + * 3. APIs to return first active call + * 4. APIs to return the connections of first active call + * 5. APIs to return other property of first active call + */ + + /** + * @return list of ringing calls + */ + public ArrayList<Call> getRingingCalls() { + return mBackgroundCalls; + } + + /** + * @return list of background calls + */ + public ArrayList<Call> getBackgroundCalls() { + return mBackgroundCalls; + } + + /** + * Return true if there is at least one active foreground call + */ + public boolean hasActiveFgCall() { + return (getFirstActiveCall(mForegroundCalls) != null); + } + + /** + * Return true if there is at least one active background call + */ + public boolean hasActiveBgCall() { + // TODO since hasActiveBgCall may get called often + // better to cache it to improve performance + return (getFirstActiveCall(mBackgroundCalls) != null); + } + + /** + * Return true if there is at least one active ringing call + * + */ + public boolean hasActiveRingingCall() { + return (getFirstActiveCall(mRingingCalls) != null); + } + + /** + * return the active foreground call from foreground calls + * + * Active call means the call is NOT in Call.State.IDLE + * + * 1. If there is active foreground call, return it + * 2. If there is no active foreground call, return the + * foreground call associated with default phone, which state is IDLE. + * 3. If there is no phone registered at all, return null. + * + */ + public Call getActiveFgCall() { + for (Call call : mForegroundCalls) { + if (call.getState() != Call.State.IDLE) { + return call; + } + } + return (mDefaultPhone == null) ? + null : mDefaultPhone.getForegroundCall(); + } + + /** + * return one active background call from background calls + * + * Active call means the call is NOT idle defined by Call.isIdle() + * + * 1. If there is only one active background call, return it + * 2. If there is more than one active background call, return the first one + * 3. If there is no active background call, return the background call + * associated with default phone, which state is IDLE. + * 4. If there is no background call at all, return null. + * + * Complete background calls list can be get by getBackgroundCalls() + */ + public Call getFirstActiveBgCall() { + for (Call call : mBackgroundCalls) { + if (!call.isIdle()) { + return call; + } + } + return (mDefaultPhone == null) ? + null : mDefaultPhone.getBackgroundCall(); + } + + /** + * return one active ringing call from ringing calls + * + * Active call means the call is NOT idle defined by Call.isIdle() + * + * 1. If there is only one active ringing call, return it + * 2. If there is more than one active ringing call, return the first one + * 3. If there is no active ringing call, return the ringing call + * associated with default phone, which state is IDLE. + * 4. If there is no ringing call at all, return null. + * + * Complete ringing calls list can be get by getRingingCalls() + */ + public Call getFirstActiveRingingCall() { + for (Call call : mRingingCalls) { + if (!call.isIdle()) { + return call; + } + } + return (mDefaultPhone == null) ? + null : mDefaultPhone.getRingingCall(); + } + + /** + * @return the state of active foreground call + * return IDLE if there is no active foreground call + */ + public Call.State getActiveFgCallState() { + Call fgCall = getActiveFgCall(); + + if (fgCall != null) { + return fgCall.getState(); + } + + return Call.State.IDLE; + } + + /** + * @return the connections of active foreground call + * return null if there is no active foreground call + */ + public List<Connection> getFgCallConnections() { + Call fgCall = getActiveFgCall(); + if ( fgCall != null) { + return fgCall.getConnections(); + } + return emptyConnections; + } + + /** + * @return the connections of active background call + * return empty list if there is no active background call + */ + public List<Connection> getBgCallConnections() { + Call bgCall = getActiveFgCall(); + if ( bgCall != null) { + return bgCall.getConnections(); + } + return emptyConnections; + } + + /** + * @return the latest connection of active foreground call + * return null if there is no active foreground call + */ + public Connection getFgCallLatestConnection() { + Call fgCall = getActiveFgCall(); + if ( fgCall != null) { + return fgCall.getLatestConnection(); + } + return null; + } + + /** + * @return true if there is at least one Foreground call in disconnected state + */ + public boolean hasDisconnectedFgCall() { + return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED) != null); + } + + /** + * @return true if there is at least one background call in disconnected state + */ + public boolean hasDisconnectedBgCall() { + return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED) != null); + } + + /** + * @return the first active call from a call list + */ + private Call getFirstActiveCall(ArrayList<Call> calls) { + for (Call call : calls) { + if (!call.isIdle()) { + return call; + } + } + return null; + } + + /** + * @return the first call in a the Call.state from a call list + */ + private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state) { + for (Call call : calls) { + if (call.getState() == state) { + return call; + } + } + return null; + } + + + + + private Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_DISCONNECT: + mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_CALL_STATE_CHANGED: + mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + } + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl index e74b9e4..5cba2e1 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl @@ -59,6 +59,11 @@ interface IPhoneSubInfo { String getVoiceMailNumber(); /** + * Retrieves the complete voice mail number. + */ + String getCompleteVoiceMailNumber(); + + /** * Retrieves the alpha identifier associated with the voice mail number. */ String getVoiceMailAlphaTag(); diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java index 4f71bb1..86c86bb 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java @@ -21,6 +21,7 @@ import java.io.PrintWriter; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; +import android.telephony.PhoneNumberUtils; import android.util.Log; public class PhoneSubInfo extends IPhoneSubInfo.Stub { @@ -29,6 +30,9 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { private Context mContext; private static final String READ_PHONE_STATE = android.Manifest.permission.READ_PHONE_STATE; + private static final String CALL_PRIVILEGED = + // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE + android.Manifest.permission.CALL_PRIVILEGED; public PhoneSubInfo(Phone phone) { mPhone = phone; @@ -101,7 +105,22 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { */ public String getVoiceMailNumber() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); - return (String) mPhone.getVoiceMailNumber(); + String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber()); + Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number); + return number; + } + + /** + * Retrieves the compelete voice mail number. + * + * @hide + */ + public String getCompleteVoiceMailNumber() { + mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED, + "Requires CALL_PRIVILEGED"); + String number = mPhone.getVoiceMailNumber(); + Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number); + return number; } /** diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java index 202ded2..7009893 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java @@ -82,6 +82,13 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub { } /** + * Retrieves the complete voice mail number. + */ + public String getCompleteVoiceMailNumber() { + return mPhoneSubInfo.getCompleteVoiceMailNumber(); + } + + /** * Retrieves the alpha identifier associated with the voice mail number. */ public String getVoiceMailAlphaTag() { |
