diff options
Diffstat (limited to 'services')
11 files changed, 1022 insertions, 665 deletions
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index 5cf61bd..e6c32d9 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -210,9 +210,6 @@ class BatteryService extends Binder { boolean logOutlier = false; long dischargeDuration = 0; - shutdownIfNoPower(); - shutdownIfOverTemp(); - mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL; if (mAcOnline) { mPlugType = BatteryManager.BATTERY_PLUGGED_AC; @@ -221,6 +218,19 @@ class BatteryService extends Binder { } else { mPlugType = BATTERY_PLUGGED_NONE; } + + // Let the battery stats keep track of the current level. + try { + mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth, + mPlugType, mBatteryLevel, mBatteryTemperature, + mBatteryVoltage); + } catch (RemoteException e) { + // Should never happen. + } + + shutdownIfNoPower(); + shutdownIfOverTemp(); + if (mBatteryStatus != mLastBatteryStatus || mBatteryHealth != mLastBatteryHealth || mBatteryPresent != mLastBatteryPresent || @@ -263,16 +273,6 @@ class BatteryService extends Binder { EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, mBatteryLevel, mBatteryVoltage, mBatteryTemperature); } - if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) { - // If the battery level has changed and we are on battery, update the current level. - // This is used for discharge cycle tracking so this shouldn't be updated while the - // battery is charging. - try { - mBatteryStats.recordCurrentLevel(mBatteryLevel); - } catch (RemoteException e) { - // Should never happen. - } - } if (mBatteryLevelCritical && !mLastBatteryLevelCritical && mPlugType == BATTERY_PLUGGED_NONE) { // We want to make sure we log discharge cycle outliers @@ -342,11 +342,6 @@ class BatteryService extends Binder { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING); - try { - mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel); - } catch (RemoteException e) { - // Should never happen. - } int icon = getIcon(mBatteryLevel); diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 72c4166..8d9bb29 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -81,6 +81,10 @@ public class InputManager { private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists); private static native void nativeRegisterInputChannel(InputChannel inputChannel); private static native void nativeUnregisterInputChannel(InputChannel inputChannel); + private static native int nativeInjectKeyEvent(KeyEvent event, int nature, + int injectorPid, int injectorUid, boolean sync, int timeoutMillis); + private static native int nativeInjectMotionEvent(MotionEvent event, int nature, + int injectorPid, int injectorUid, boolean sync, int timeoutMillis); // Device class as defined by EventHub. private static final int CLASS_KEYBOARD = 0x00000001; @@ -90,6 +94,12 @@ public class InputManager { private static final int CLASS_TOUCHSCREEN_MT = 0x00000010; private static final int CLASS_DPAD = 0x00000020; + // Input event injection constants defined in InputDispatcher.h. + static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0; + static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1; + static final int INPUT_EVENT_INJECTION_FAILED = 2; + static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3; + public InputManager(Context context, WindowManagerService windowManagerService, WindowManagerPolicy windowManagerPolicy, @@ -215,37 +225,63 @@ public class InputManager { nativeUnregisterInputChannel(inputChannel); } - // TBD where this really belongs, duplicate copy in WindowManagerService - static final int INJECT_FAILED = 0; - static final int INJECT_SUCCEEDED = 1; - static final int INJECT_NO_PERMISSION = -1; - /** * Injects a key event into the event system on behalf of an application. + * This method may block even if sync is false because it must wait for previous events + * to be dispatched before it can determine whether input event injection will be + * permitted based on the current input focus. * @param event The event to inject. * @param nature The nature of the event. + * @param injectorPid The pid of the injecting application. + * @param injectorUid The uid of the injecting application. * @param sync If true, waits for the event to be completed before returning. - * @param pid The pid of the injecting application. - * @param uid The uid of the injecting application. - * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION + * @param timeoutMillis The injection timeout in milliseconds. + * @return One of the INPUT_EVENT_INJECTION_XXX constants. */ - public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) { - // TODO - return INJECT_FAILED; + public int injectKeyEvent(KeyEvent event, int nature, int injectorPid, int injectorUid, + boolean sync, int timeoutMillis) { + if (event == null) { + throw new IllegalArgumentException("event must not be null"); + } + if (injectorPid < 0 || injectorUid < 0) { + throw new IllegalArgumentException("injectorPid and injectorUid must not be negative."); + } + if (timeoutMillis <= 0) { + throw new IllegalArgumentException("timeoutMillis must be positive"); + } + + return nativeInjectKeyEvent(event, nature, injectorPid, injectorUid, + sync, timeoutMillis); } /** * Injects a motion event into the event system on behalf of an application. + * This method may block even if sync is false because it must wait for previous events + * to be dispatched before it can determine whether input event injection will be + * permitted based on the current input focus. * @param event The event to inject. * @param nature The nature of the event. * @param sync If true, waits for the event to be completed before returning. - * @param pid The pid of the injecting application. - * @param uid The uid of the injecting application. - * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION + * @param injectorPid The pid of the injecting application. + * @param injectorUid The uid of the injecting application. + * @param sync If true, waits for the event to be completed before returning. + * @param timeoutMillis The injection timeout in milliseconds. + * @return One of the INPUT_EVENT_INJECTION_XXX constants. */ - public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) { - // TODO - return INJECT_FAILED; + public int injectMotionEvent(MotionEvent event, int nature, int injectorPid, int injectorUid, + boolean sync, int timeoutMillis) { + if (event == null) { + throw new IllegalArgumentException("event must not be null"); + } + if (injectorPid < 0 || injectorUid < 0) { + throw new IllegalArgumentException("injectorPid and injectorUid must not be negative."); + } + if (timeoutMillis <= 0) { + throw new IllegalArgumentException("timeoutMillis must be positive"); + } + + return nativeInjectMotionEvent(event, nature, injectorPid, injectorUid, + sync, timeoutMillis); } public void dump(PrintWriter pw) { @@ -271,8 +307,6 @@ public class InputManager { private static final boolean DEBUG_VIRTUAL_KEYS = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; - private final InputTargetList mReusableInputTargetList = new InputTargetList(); - @SuppressWarnings("unused") public boolean isScreenOn() { return mPowerManagerService.isScreenOn(); @@ -309,6 +343,21 @@ public class InputManager { } @SuppressWarnings("unused") + public void notifyInputChannelBroken(InputChannel inputChannel) { + mWindowManagerService.notifyInputChannelBroken(inputChannel); + } + + @SuppressWarnings("unused") + public long notifyInputChannelANR(InputChannel inputChannel) { + return mWindowManagerService.notifyInputChannelANR(inputChannel); + } + + @SuppressWarnings("unused") + public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) { + mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel); + } + + @SuppressWarnings("unused") public int hackInterceptKey(int deviceId, int type, int scanCode, int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) { RawInputEvent event = new RawInputEvent(); @@ -437,24 +486,23 @@ public class InputManager { return names.toArray(new String[names.size()]); } + // TODO All code related to target identification should be moved down into native. @SuppressWarnings("unused") - public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) { - mReusableInputTargetList.clear(); - - mWindowManagerService.getKeyEventTargets(mReusableInputTargetList, - event, nature, policyFlags); - - return mReusableInputTargetList.toNullTerminatedArray(); + public int getKeyEventTargets(InputTargetList inputTargets, + KeyEvent event, int nature, int policyFlags, + int injectorPid, int injectorUid) { + inputTargets.clear(); + return mWindowManagerService.getKeyEventTargetsTd( + inputTargets, event, nature, policyFlags, injectorPid, injectorUid); } @SuppressWarnings("unused") - public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) { - mReusableInputTargetList.clear(); - - mWindowManagerService.getMotionEventTargets(mReusableInputTargetList, - event, nature, policyFlags); - - return mReusableInputTargetList.toNullTerminatedArray(); + public int getMotionEventTargets(InputTargetList inputTargets, + MotionEvent event, int nature, int policyFlags, + int injectorPid, int injectorUid) { + inputTargets.clear(); + return mWindowManagerService.getMotionEventTargetsTd( + inputTargets, event, nature, policyFlags, injectorPid, injectorUid); } } } diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java index 1575612..83acc8f 100644 --- a/services/java/com/android/server/InputTargetList.java +++ b/services/java/com/android/server/InputTargetList.java @@ -29,7 +29,7 @@ import android.view.InputTarget; * * @hide */ -public class InputTargetList { +public final class InputTargetList { private InputTarget[] mArray; private int mCount; @@ -55,7 +55,7 @@ public class InputTargetList { count -= 1; mArray[count].recycle(); } - // mArray[0] could be set to null here but we do it in toNullTerminatedArray() + mArray[0] = null; } /** @@ -91,7 +91,7 @@ public class InputTargetList { mArray[mCount] = inputTarget; mCount += 1; - // mArray[mCount] could be set to null here but we do it in toNullTerminatedArray() + mArray[mCount] = null; } /** @@ -99,7 +99,6 @@ public class InputTargetList { * @return The input target array. */ public InputTarget[] toNullTerminatedArray() { - mArray[mCount] = null; return mArray; } }
\ No newline at end of file diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index 9493161..431cc39 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -44,6 +44,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.provider.Settings; import android.text.format.DateUtils; import android.text.format.Time; @@ -64,11 +65,13 @@ class UiModeManagerService extends IUiModeManager.Stub { private static final int MSG_UPDATE_TWILIGHT = 0; private static final int MSG_ENABLE_LOCATION_UPDATES = 1; + private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; - private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; + private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS; + private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20; private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000; - private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 15 * DateUtils.MINUTE_IN_MILLIS; private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS; private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE"; @@ -215,6 +218,21 @@ class UiModeManagerService extends IUiModeManager.Stub { } }; + private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) { + if (!intent.getBooleanExtra("state", false)) { + // Airplane mode is now off! + mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE); + } + } else { + // Time zone has changed! + mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE); + } + } + }; + // A LocationListener to initialize the network location provider. The location updates // are handled through the passive location provider. private final LocationListener mEmptyLocationListener = new LocationListener() { @@ -304,6 +322,9 @@ class UiModeManagerService extends IUiModeManager.Stub { new IntentFilter(Intent.ACTION_DOCK_EVENT)); mContext.registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + mContext.registerReceiver(mUpdateLocationReceiver, filter); PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); @@ -586,7 +607,9 @@ class UiModeManagerService extends IUiModeManager.Stub { boolean mPassiveListenerEnabled; boolean mNetworkListenerEnabled; - + boolean mDidFirstInit; + long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS; + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -599,6 +622,25 @@ class UiModeManagerService extends IUiModeManager.Stub { } } break; + case MSG_GET_NEW_LOCATION_UPDATE: + if (!mNetworkListenerEnabled) { + // Don't do anything -- we are still trying to get a + // location. + return; + } + if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS) + >= SystemClock.elapsedRealtime()) { + // Don't do anything -- it hasn't been long enough + // since we last requested an update. + return; + } + + // Unregister the current location monitor, so we can + // register a new one for it to get an immediate update. + mNetworkListenerEnabled = false; + mLocationManager.removeUpdates(mEmptyLocationListener); + + // Fall through to re-register listener. case MSG_ENABLE_LOCATION_UPDATES: // enable network provider to receive at least location updates for a given // distance. @@ -613,17 +655,21 @@ class UiModeManagerService extends IUiModeManager.Stub { } if (!mNetworkListenerEnabled && networkLocationEnabled) { mNetworkListenerEnabled = true; + mLastNetworkRegisterTime = SystemClock.elapsedRealtime(); mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, LOCATION_UPDATE_MS, 0, mEmptyLocationListener); - if (mLocation == null) { - retrieveLocation(); - } - synchronized (mLock) { - if (isDoingNightMode() && mLocation != null - && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - updateTwilightLocked(); - updateLocked(0, 0); + if (!mDidFirstInit) { + mDidFirstInit = true; + if (mLocation == null) { + retrieveLocation(); + } + synchronized (mLock) { + if (isDoingNightMode() && mLocation != null + && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + updateTwilightLocked(); + updateLocked(0, 0); + } } } } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 9bc3931..95ab5bc 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -203,6 +203,10 @@ public class WindowManagerService extends IWindowManager.Stub /** Adjustment to time to perform a dim, to make it more dramatic. */ static final int DIM_DURATION_MULTIPLIER = 6; + + // Maximum number of milliseconds to wait for input event injection. + // FIXME is this value reasonable? + private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; static final int INJECT_FAILED = 0; static final int INJECT_SUCCEEDED = 1; @@ -447,8 +451,6 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>(); final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>(); - //flag to detect fat touch events - boolean mFatTouch = false; Display mDisplay; H mH = new H(); @@ -5072,106 +5074,336 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.adjustConfigurationLw(config); return true; } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public void notifyInputChannelBroken(InputChannel inputChannel) { + synchronized (mWindowMap) { + WindowState windowState = getWindowStateForInputChannelLocked(inputChannel); + if (windowState == null) { + return; // irrelevant + } + + Slog.i(TAG, "WINDOW DIED " + windowState); + removeWindowLocked(windowState.mSession, windowState); + } + } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public long notifyInputChannelANR(InputChannel inputChannel) { + IApplicationToken appToken; + synchronized (mWindowMap) { + WindowState windowState = getWindowStateForInputChannelLocked(inputChannel); + if (windowState == null) { + return -2; // irrelevant, abort dispatching (-2) + } + + Slog.i(TAG, "Input event dispatching timed out sending to " + + windowState.mAttrs.getTitle()); + appToken = windowState.getAppToken(); + } + + try { + // Notify the activity manager about the timeout and let it decide whether + // to abort dispatching or keep waiting. + boolean abort = appToken.keyDispatchingTimedOut(); + if (abort) { + return -2; // abort dispatching + } + + // Return new timeout. + // We use -1 for infinite timeout to avoid clash with -2 magic number. + long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000; + return newTimeout < 0 ? -1 : newTimeout; + } catch (RemoteException ex) { + return -2; // abort dispatching + } + } + + /* Notifies the window manager about a broken input channel. + * + * Called by the InputManager. + */ + public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) { + // Nothing to do just now. + // Just wait for the user to dismiss the ANR dialog. + + // TODO We could try to automatically dismiss the ANR dialog on recovery + // although that might be disorienting. + } + + private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) { + int windowCount = mWindows.size(); + for (int i = 0; i < windowCount; i++) { + WindowState windowState = (WindowState) mWindows.get(i); + if (windowState.mInputChannel == inputChannel) { + return windowState; + } + } + + return null; + } // ------------------------------------------------------------- // Input Events and Focus Management // ------------------------------------------------------------- - public void getKeyEventTargets(InputTargetList inputTargets, - KeyEvent event, int nature, int policyFlags) { + private boolean checkInjectionPermissionTd(WindowState focus, + int injectorPid, int injectorUid) { + if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) { + if (mContext.checkPermission( + android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid) + != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Permission denied: injecting key event from pid " + + injectorPid + " uid " + injectorUid + " to window " + focus + + " owned by uid " + focus.mSession.mUid); + return false; + } + } + return true; + } + + /* Gets the input targets for a key event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + public int getKeyEventTargetsTd(InputTargetList inputTargets, + KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) { if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event); // TODO what do we do with mDisplayFrozen? // TODO what do we do with focus.mToken.paused? WindowState focus = getFocusedWindow(); + + if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) { + return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED; + } + + if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(), + event.getAction() == KeyEvent.ACTION_DOWN, + event.getRepeatCount(), event.getFlags())) { + // Policy consumed the event. + return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; + } + + if (focus == null) { + return InputManager.INPUT_EVENT_INJECTION_FAILED; + } + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC); + addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC); + return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; } - // Target of Motion events - WindowState mTouchFocus; - - // Windows above the target who would like to receive an "outside" - // touch event for any down events outside of them. - // (This is a linked list by way of WindowState.mNextOutsideTouch.) - WindowState mOutsideTouchTargets; - - private void clearTouchFocus() { - mTouchFocus = null; - mOutsideTouchTargets = null; + /* Gets the input targets for a motion event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + public int getMotionEventTargetsTd(InputTargetList inputTargets, + MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) { + switch (nature) { + case InputQueue.INPUT_EVENT_NATURE_TRACKBALL: + return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags, + injectorPid, injectorUid); + + case InputQueue.INPUT_EVENT_NATURE_TOUCH: + return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags, + injectorPid, injectorUid); + + default: + return InputManager.INPUT_EVENT_INJECTION_FAILED; + } } - public void getMotionEventTargets(InputTargetList inputTargets, - MotionEvent event, int nature, int policyFlags) { - if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) { - // More or less the same as for keys... - WindowState focus = getFocusedWindow(); - wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - - addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC); - return; + /* Gets the input targets for a trackball event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets, + MotionEvent event, int policyFlags, int injectorPid, int injectorUid) { + WindowState focus = getFocusedWindow(); + + if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) { + return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED; } - int action = event.getAction(); - - // TODO detect cheek presses somewhere... either here or in native code + if (focus == null) { + return InputManager.INPUT_EVENT_INJECTION_FAILED; + } - final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; + wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - WindowState target = mTouchFocus; + addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC); + return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; + } + + /* Set to true when a fat touch has been detected during the processing of a touch event. + * + * Only used by getMotionEventTargetsForTouchTd. + * Set to true whenever a fat touch is detected and reset to false on ACTION_UP. + */ + private boolean mFatTouch; + + /* Set to true when we think the touch event. + * + * Only used by getMotionEventTargetsForTouchTd. + * Set to true on ACTION_DOWN and set to false on ACTION_UP. + */ + private boolean mTouchDown; + + /* Current target of Motion events. + * + * Only used by getMotionEventTargetsForTouchTd. + * Initialized on ACTION_DOWN and cleared on ACTION_UP. + */ + private WindowState mTouchFocus; + + /* Windows above the target that would like to receive an "outside" touch event + * for any down events outside of them. + * + * Only used by getMotionEventTargetsForTouchTd. + * Initialized on ACTION_DOWN and cleared immediately afterwards. + */ + private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>(); + + /* Wallpaper windows that are currently receiving touch events. + * + * Only used by getMotionEventTargetsForTouchTd. + * Initialized on ACTION_DOWN and cleared on ACTION_UP. + */ + private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>(); + + /* Gets the input targets for a touch event. + * + * Called by the InputManager on the InputDispatcher thread. + */ + private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets, + MotionEvent event, int policyFlags, int injectorPid, int injectorUid) { + final int action = event.getAction(); - if (action == MotionEvent.ACTION_UP) { - // let go of our target - mPowerManager.logPointerUpEvent(); - clearTouchFocus(); - } else if (action == MotionEvent.ACTION_DOWN) { - // acquire a new target - mPowerManager.logPointerDownEvent(); + if (action == MotionEvent.ACTION_DOWN) { + updateTouchFocusBeforeDownTd(event, policyFlags); + } else { + updateTouchFocusBeforeNonDownTd(event, policyFlags); + } + + boolean skipDelivery = false; + int touchTargetFlags = 0; - synchronized (mWindowMap) { - if (mTouchFocus != null) { - // this is weird, we got a pen down, but we thought it was - // already down! - // XXX: We should probably send an ACTION_UP to the current - // target. - Slog.w(TAG, "Pointer down received while already down in: " - + mTouchFocus); - clearTouchFocus(); - } - - // ACTION_DOWN is special, because we need to lock next events to - // the window we'll land onto. - final int x = (int) event.getX(); - final int y = (int) event.getY(); - - final ArrayList windows = mWindows; - final int N = windows.size(); - WindowState topErrWindow = null; - final Rect tmpRect = mTempRect; - for (int i=N-1; i>=0; i--) { - WindowState child = (WindowState)windows.get(i); - //Slog.i(TAG, "Checking dispatch to: " + child); - final int flags = child.mAttrs.flags; - if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { - if (topErrWindow == null) { - topErrWindow = child; + int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED; + WindowState focusedTouchTarget = mTouchFocus; + if (focusedTouchTarget == null) { + // In this case we are either dropping the event, or have received + // a move or up without a down. It is common to receive move + // events in such a way, since this means the user is moving the + // pointer without actually pressing down. All other cases should + // be atypical, so let's log them. + if (action != MotionEvent.ACTION_MOVE) { + Slog.w(TAG, "No window to dispatch pointer action " + action); + injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED; + } + } else { + // We have a valid focused touch target. + if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) { + return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED; + } + + wakeupIfNeeded(focusedTouchTarget, eventType(event)); + + if ((focusedTouchTarget.mAttrs.flags & + WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { + // Target wants to ignore fat touch events + boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event); + + if (cheekPress) { + if ((action == MotionEvent.ACTION_DOWN)) { + mFatTouch = true; + skipDelivery = true; + } else { + if (! mFatTouch) { + // cancel the earlier event + touchTargetFlags |= InputTarget.FLAG_CANCEL; + mFatTouch = true; + } else { + skipDelivery = true; } } - if (!child.isVisibleLw()) { - //Slog.i(TAG, "Not visible!"); - continue; - } - if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - //Slog.i(TAG, "Not touchable!"); - if ((flags & WindowManager.LayoutParams - .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { - child.mNextOutsideTouch = mOutsideTouchTargets; - mOutsideTouchTargets = child; - } - continue; + } + } + } + + if (! skipDelivery) { + int outsideTargetCount = mOutsideTouchTargets.size(); + for (int i = 0; i < outsideTargetCount; i++) { + WindowState outsideTouchTarget = mOutsideTouchTargets.get(i); + addInputTargetTd(inputTargets, outsideTouchTarget, + InputTarget.FLAG_OUTSIDE | touchTargetFlags); + } + + int wallpaperTargetCount = mWallpaperTouchTargets.size(); + for (int i = 0; i < wallpaperTargetCount; i++) { + WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i); + addInputTargetTd(inputTargets, wallpaperTouchTarget, + touchTargetFlags); + } + + if (focusedTouchTarget != null) { + addInputTargetTd(inputTargets, focusedTouchTarget, + InputTarget.FLAG_SYNC | touchTargetFlags); + } + } + + if (action == MotionEvent.ACTION_UP) { + updateTouchFocusAfterUpTd(event, policyFlags); + } + + return injectionResult; + } + + private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) { + if (mTouchDown) { + // This is weird, we got a down, but we thought it was already down! + // XXX: We should probably send an ACTION_UP to the current target. + Slog.w(TAG, "Pointer down received while already down in: " + mTouchFocus); + updateTouchFocusAfterUpTd(event, policyFlags); + } + + mTouchDown = true; + mPowerManager.logPointerDownEvent(); + + final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; + synchronized (mWindowMap) { + final int x = (int) event.getX(); + final int y = (int) event.getY(); + + final ArrayList windows = mWindows; + final int N = windows.size(); + WindowState topErrWindow = null; + final Rect tmpRect = mTempRect; + for (int i= N - 1; i >= 0; i--) { + WindowState child = (WindowState) windows.get(i); + //Slog.i(TAG, "Checking dispatch to: " + child); + + final int flags = child.mAttrs.flags; + if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { + if (topErrWindow == null) { + topErrWindow = child; } + } + + if (!child.isVisibleLw()) { + //Slog.i(TAG, "Not visible!"); + continue; + } + + if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) { tmpRect.set(child.mFrame); if (child.mTouchableInsets == ViewTreeObserver .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) { @@ -5197,7 +5429,7 @@ public class WindowManagerService extends IWindowManager.Stub |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); if (tmpRect.contains(x, y) || touchFlags == 0) { //Slog.i(TAG, "Using this target!"); - if (!screenWasOff || (flags & + if (! screenWasOff || (flags & WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) { mTouchFocus = child; } else { @@ -5206,143 +5438,76 @@ public class WindowManagerService extends IWindowManager.Stub } break; } - - if ((flags & WindowManager.LayoutParams - .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { - child.mNextOutsideTouch = mOutsideTouchTargets; - mOutsideTouchTargets = child; - //Slog.i(TAG, "Adding to outside target list: " + child); - } } - // if there's an error window but it's not accepting - // focus (typically because it is not yet visible) just - // wait for it -- any other focused window may in fact - // be in ANR state. - if (topErrWindow != null && mTouchFocus != topErrWindow) { - mTouchFocus = null; + if ((flags & WindowManager.LayoutParams + .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { + //Slog.i(TAG, "Adding to outside target list: " + child); + mOutsideTouchTargets.add(child); } } - - target = mTouchFocus; - } - - if (target != null) { - wakeupIfNeeded(target, eventType(event)); - } - - int targetFlags = 0; - if (target == null) { - // In this case we are either dropping the event, or have received - // a move or up without a down. It is common to receive move - // events in such a way, since this means the user is moving the - // pointer without actually pressing down. All other cases should - // be atypical, so let's log them. - if (action != MotionEvent.ACTION_MOVE) { - Slog.w(TAG, "No window to dispatch pointer action " + action); - } - } else { - if ((target.mAttrs.flags & - WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { - //target wants to ignore fat touch events - boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event); - //explicit flag to return without processing event further - boolean returnFlag = false; - if((action == MotionEvent.ACTION_DOWN)) { - mFatTouch = false; - if(cheekPress) { - mFatTouch = true; - returnFlag = true; - } - } else { - if(action == MotionEvent.ACTION_UP) { - if(mFatTouch) { - //earlier even was invalid doesnt matter if current up is cheekpress or not - mFatTouch = false; - returnFlag = true; - } else if(cheekPress) { - //cancel the earlier event - targetFlags |= InputTarget.FLAG_CANCEL; - action = MotionEvent.ACTION_CANCEL; - } - } else if(action == MotionEvent.ACTION_MOVE) { - if(mFatTouch) { - //two cases here - //an invalid down followed by 0 or moves(valid or invalid) - //a valid down, invalid move, more moves. want to ignore till up - returnFlag = true; - } else if(cheekPress) { - //valid down followed by invalid moves - //an invalid move have to cancel earlier action - targetFlags |= InputTarget.FLAG_CANCEL; - action = MotionEvent.ACTION_CANCEL; - if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE"); - //note that the subsequent invalid moves will not get here - mFatTouch = true; - } - } - } //else if action - if(returnFlag) { - return; - } - } //end if target - } - - synchronized (mWindowMap) { - if (target != null && ! target.isVisibleLw()) { - target = null; + + // If there's an error window but it's not accepting focus (typically because + // it is not yet visible) just wait for it -- any other focused window may in fact + // be in ANR state. + if (topErrWindow != null && mTouchFocus != topErrWindow) { + mTouchFocus = null; } - if (action == MotionEvent.ACTION_DOWN) { - while (mOutsideTouchTargets != null) { - addInputTarget(inputTargets, mOutsideTouchTargets, - InputTarget.FLAG_OUTSIDE | targetFlags); - mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch; - } + // Drop the touch focus if the window is not visible. + if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) { + mTouchFocus = null; } - // If we sent an initial down to the wallpaper, then continue - // sending events until the final up. - // Alternately if we are on top of the wallpaper, then the wallpaper also - // gets to see this movement. - if (mSendingPointersToWallpaper || - (target != null && action == MotionEvent.ACTION_DOWN - && mWallpaperTarget == target - && target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) { + // Determine wallpaper targets. + if (mTouchFocus != null + && mTouchFocus == mWallpaperTarget + && mTouchFocus.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) { int curTokenIndex = mWallpaperTokens.size(); while (curTokenIndex > 0) { curTokenIndex--; WindowToken token = mWallpaperTokens.get(curTokenIndex); + int curWallpaperIndex = token.windows.size(); while (curWallpaperIndex > 0) { curWallpaperIndex--; WindowState wallpaper = token.windows.get(curWallpaperIndex); if ((wallpaper.mAttrs.flags & - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - continue; - } - - switch (action) { - case MotionEvent.ACTION_DOWN: - mSendingPointersToWallpaper = true; - break; - case MotionEvent.ACTION_UP: - mSendingPointersToWallpaper = false; - break; + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) { + mWallpaperTouchTargets.add(wallpaper); } - - addInputTarget(inputTargets, wallpaper, targetFlags); } } } - - if (target != null) { - addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags); + } + } + + private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) { + synchronized (mWindowMap) { + // Drop the touch focus if the window is not visible. + if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) { + mTouchFocus = null; + mWallpaperTouchTargets.clear(); } } } - private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) { + private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) { + mFatTouch = false; + mTouchDown = false; + mTouchFocus = null; + mOutsideTouchTargets.clear(); + mWallpaperTouchTargets.clear(); + + mPowerManager.logPointerUpEvent(); + } + + /* Adds a window to a list of input targets. + * Do NOT call this method while holding any locks because the call to + * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager + * and create a deadlock hazard. + */ + private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) { if (window.mInputChannel == null) { return; } @@ -5874,8 +6039,8 @@ public class WindowManagerService extends IWindowManager.Stub final int result; if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectKeyEvent(newEvent, - InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid); + result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY, + pid, uid, sync, INJECTION_TIMEOUT_MILLIS); } else { result = dispatchKey(newEvent, pid, uid); if (sync) { @@ -5884,14 +6049,7 @@ public class WindowManagerService extends IWindowManager.Stub } Binder.restoreCallingIdentity(ident); - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; - } - return false; + return reportInjectionResult(result); } /** @@ -5910,8 +6068,8 @@ public class WindowManagerService extends IWindowManager.Stub final int result; if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectMotionEvent(ev, - InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid); + result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH, + pid, uid, sync, INJECTION_TIMEOUT_MILLIS); } else { result = dispatchPointer(null, ev, pid, uid); if (sync) { @@ -5920,14 +6078,7 @@ public class WindowManagerService extends IWindowManager.Stub } Binder.restoreCallingIdentity(ident); - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; - } - return false; + return reportInjectionResult(result); } /** @@ -5946,8 +6097,8 @@ public class WindowManagerService extends IWindowManager.Stub final int result; if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectMotionEvent(ev, - InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid); + result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL, + pid, uid, sync, INJECTION_TIMEOUT_MILLIS); } else { result = dispatchTrackball(null, ev, pid, uid); if (sync) { @@ -5956,14 +6107,37 @@ public class WindowManagerService extends IWindowManager.Stub } Binder.restoreCallingIdentity(ident); - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; + return reportInjectionResult(result); + } + + private boolean reportInjectionResult(int result) { + if (ENABLE_NATIVE_INPUT_DISPATCH) { + switch (result) { + case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED: + Slog.w(TAG, "Input event injection permission denied."); + throw new SecurityException( + "Injecting to another application requires INJECT_EVENTS permission"); + case InputManager.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."); + return false; + case InputManager.INPUT_EVENT_INJECTION_FAILED: + default: + Slog.w(TAG, "Input event injection failed."); + return false; + } + } else { + switch (result) { + case INJECT_NO_PERMISSION: + throw new SecurityException( + "Injecting to another application requires INJECT_EVENTS permission"); + case INJECT_SUCCEEDED: + return true; + } + return false; } - return false; } private WindowState getFocusedWindow() { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index ec209ed..d59ecdd 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -8725,6 +8725,35 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public void setImmersive(IBinder token, boolean immersive) { + synchronized(this) { + int index = (token != null) ? indexOfTokenLocked(token) : -1; + if (index < 0) { + throw new IllegalArgumentException(); + } + HistoryRecord r = (HistoryRecord)mHistory.get(index); + r.immersive = immersive; + } + } + + public boolean isImmersive(IBinder token) { + synchronized (this) { + int index = (token != null) ? indexOfTokenLocked(token) : -1; + if (index < 0) { + throw new IllegalArgumentException(); + } + HistoryRecord r = (HistoryRecord)mHistory.get(index); + return r.immersive; + } + } + + public boolean isTopActivityImmersive() { + synchronized (this) { + HistoryRecord r = topRunningActivityLocked(null); + return (r != null) ? r.immersive : false; + } + } + public final void enterSafeMode() { synchronized(this) { // It only makes sense to do this before the system is ready @@ -9538,7 +9567,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen sb.append("Subject: ").append(subject).append("\n"); } sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); - if (crashInfo.durationMillis != -1) { + if (crashInfo != null && crashInfo.durationMillis != -1) { sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n"); } sb.append("\n"); diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index ba13c51..37da6f7 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -59,7 +59,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public void shutdown() { Slog.w("BatteryStats", "Writing battery stats before shutdown..."); synchronized (mStats) { - mStats.writeLocked(); + mStats.shutdownLocked(); } } @@ -96,7 +96,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public void noteStartWakelock(int uid, String name, int type) { enforceCallingPermission(); synchronized (mStats) { - Slog.i("battery", "Start wake lock: " + uid + " " + name); mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type); } } @@ -104,7 +103,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public void noteStopWakelock(int uid, String name, int type) { enforceCallingPermission(); synchronized (mStats) { - Slog.i("battery", "Stop wake lock: " + uid + " " + name); mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type); } } @@ -126,14 +124,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub { public void noteStartGps(int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteStartGps(uid); + mStats.noteStartGpsLocked(uid); } } public void noteStopGps(int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteStopGps(uid); + mStats.noteStopGpsLocked(uid); } } @@ -323,14 +321,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub { return mStats.isOnBattery(); } - public void setOnBattery(boolean onBattery, int level) { + public void setBatteryState(int status, int health, int plugType, int level, + int temp, int volt) { enforceCallingPermission(); - mStats.setOnBattery(onBattery, level); - } - - public void recordCurrentLevel(int level) { - enforceCallingPermission(); - mStats.recordCurrentLevel(level); + mStats.setBatteryState(status, health, plugType, level, temp, volt); } public long getAwakeTimeBattery() { @@ -361,7 +355,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub { for (String arg : args) { if ("--checkin".equals(arg)) { isCheckin = true; - break; + } else if ("--reset".equals(arg)) { + mStats.resetAllStatsLocked(); } } } diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index dca7a99..fb5c8aa 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -101,6 +101,7 @@ class HistoryRecord extends IApplicationToken.Stub { boolean idle; // has the activity gone idle? boolean hasBeenLaunched;// has this activity ever been launched? boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. + boolean immersive; // immersive mode (don't interrupt if possible) String stringName; // for caching of toString(). @@ -153,6 +154,7 @@ class HistoryRecord extends IApplicationToken.Stub { pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused); pw.print(" inHistory="); pw.print(inHistory); pw.print(" persistent="); pw.print(persistent); + pw.print(" immersive="); pw.print(immersive); pw.print(" launchMode="); pw.println(launchMode); pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen); pw.print(" visible="); pw.print(visible); @@ -278,6 +280,8 @@ class HistoryRecord extends IApplicationToken.Stub { } else { isHomeActivity = false; } + + immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0; } else { realActivity = null; taskAffinity = null; @@ -289,6 +293,7 @@ class HistoryRecord extends IApplicationToken.Stub { packageName = null; fullscreen = true; isHomeActivity = false; + immersive = false; } } diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 0cf67a3..4b4c784 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -226,8 +226,6 @@ public class GpsLocationProvider implements LocationProviderInterface { private Handler mHandler; // Used to signal when our main thread has initialized everything private final CountDownLatch mInitializedLatch = new CountDownLatch(1); - // Thread for receiving events from the native code - private Thread mEventThread; private String mAGpsApn; private int mAGpsDataConnectionState; @@ -643,10 +641,6 @@ public class GpsLocationProvider implements LocationProviderInterface { if (mC2KServerHost != null) { native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort); } - - // run event listener thread while we are enabled - mEventThread = new GpsEventThread(); - mEventThread.start(); } else { Log.w(TAG, "Failed to enable location provider"); } @@ -669,29 +663,9 @@ public class GpsLocationProvider implements LocationProviderInterface { mEnabled = false; stopNavigating(); - native_disable(); - - // make sure our event thread exits - if (mEventThread != null) { - try { - mEventThread.join(); - } catch (InterruptedException e) { - Log.w(TAG, "InterruptedException when joining mEventThread"); - } - mEventThread = null; - } // do this before releasing wakelock native_cleanup(); - - // The GpsEventThread does not wait for the GPS to shutdown - // so we need to report the GPS_STATUS_ENGINE_OFF event here - if (mNavigating) { - reportStatus(GPS_STATUS_SESSION_END); - } - if (mEngineOn) { - reportStatus(GPS_STATUS_ENGINE_OFF); - } } public boolean isEnabled() { @@ -1231,12 +1205,12 @@ public class GpsLocationProvider implements LocationProviderInterface { /** * called from native code to report NMEA data received */ - private void reportNmea(int index, long timestamp) { + private void reportNmea(long timestamp) { synchronized(mListeners) { int size = mListeners.size(); if (size > 0) { // don't bother creating the String if we have no listeners - int length = native_read_nmea(index, mNmeaBuffer, mNmeaBuffer.length); + int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length); String nmea = new String(mNmeaBuffer, 0, length); for (int i = 0; i < size; i++) { @@ -1359,28 +1333,6 @@ public class GpsLocationProvider implements LocationProviderInterface { mNIHandler.handleNiNotification(notification); } - // this thread is used to receive events from the native code. - // native_wait_for_event() will callback to us via reportLocation(), reportStatus(), etc. - // this is necessary because native code cannot call Java on a thread that the JVM does - // not know about. - private final class GpsEventThread extends Thread { - - public GpsEventThread() { - super("GpsEventThread"); - } - - public void run() { - if (DEBUG) Log.d(TAG, "GpsEventThread starting"); - // Exit as soon as disable() is called instead of waiting for the GPS to stop. - while (mEnabled) { - // this will wait for an event from the GPS, - // which will be reported via reportLocation or reportStatus - native_wait_for_event(); - } - if (DEBUG) Log.d(TAG, "GpsEventThread exiting"); - } - } - private void sendMessage(int message, int arg, Object obj) { // hold a wake lock while messages are pending synchronized (mWakeLock) { @@ -1485,19 +1437,17 @@ public class GpsLocationProvider implements LocationProviderInterface { private static native boolean native_is_supported(); private native boolean native_init(); - private native void native_disable(); private native void native_cleanup(); private native boolean native_set_position_mode(int mode, int recurrence, int min_interval, int preferred_accuracy, int preferred_time); private native boolean native_start(); private native boolean native_stop(); private native void native_delete_aiding_data(int flags); - private native void native_wait_for_event(); // returns number of SVs // mask[0] is ephemeris mask and mask[1] is almanac mask private native int native_read_sv_status(int[] svs, float[] snrs, float[] elevations, float[] azimuths, int[] masks); - private native int native_read_nmea(int index, byte[] buffer, int bufferSize); + private native int native_read_nmea(byte[] buffer, int bufferSize); private native void native_inject_location(double latitude, double longitude, float accuracy); // XTRA Support diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index a988a96..1a6119a 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -50,6 +50,9 @@ static struct { jmethodID isScreenBright; jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; + jmethodID notifyInputChannelBroken; + jmethodID notifyInputChannelANR; + jmethodID notifyInputChannelRecoveredFromANR; jmethodID virtualKeyFeedback; jmethodID hackInterceptKey; jmethodID goToSleep; @@ -73,6 +76,13 @@ static struct { jfieldID height; } gVirtualKeyDefinitionClassInfo; +static struct { + jclass clazz; + + jmethodID ctor; + jfieldID mArray; +} gInputTargetListClassInfo; + // ---------------------------------------------------------------------------- class NativeInputManager : public virtual RefBase, @@ -89,6 +99,10 @@ public: void setDisplaySize(int32_t displayId, int32_t width, int32_t height); void setDisplayOrientation(int32_t displayId, int32_t orientation); + status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, + jweak inputChannelObjWeak); + status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); + /* --- InputReaderPolicyInterface implementation --- */ virtual bool getDisplayInfo(int32_t displayId, @@ -112,18 +126,20 @@ public: virtual void notifyConfigurationChanged(nsecs_t when); virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel); - virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel); + virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel, + nsecs_t& outNewTimeout); virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel); virtual nsecs_t getKeyRepeatTimeout(); - virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets); - virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets); + virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); + virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); private: sp<InputManager> mInputManager; jobject mCallbacksObj; + jobject mReusableInputTargetListObj; // Cached filtering policies. int32_t mFilterTouchEvents; @@ -138,12 +154,20 @@ private: bool isScreenOn(); bool isScreenBright(); + // Weak references to all currently registered input channels by receive fd. + Mutex mInputChannelRegistryLock; + KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd; + + jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel); + static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); } static bool isAppSwitchKey(int32_t keyCode); - static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName); + static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); + static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj, + Vector<InputTarget>& outTargets); }; // ---------------------------------------------------------------------------- @@ -155,6 +179,11 @@ NativeInputManager::NativeInputManager(jobject callbacksObj) : mCallbacksObj = env->NewGlobalRef(callbacksObj); + jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz, + gInputTargetListClassInfo.ctor); + mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj); + env->DeleteLocalRef(inputTargetListObj); + sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); } @@ -163,13 +192,14 @@ NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); env->DeleteGlobalRef(mCallbacksObj); + env->DeleteGlobalRef(mReusableInputTargetListObj); } bool NativeInputManager::isAppSwitchKey(int32_t keyCode) { return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL; } -bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) { +bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { LOGE("An exception was thrown by callback '%s'.", methodName); LOGE_EX(env); @@ -196,6 +226,86 @@ void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orient } } +status_t NativeInputManager::registerInputChannel(JNIEnv* env, + const sp<InputChannel>& inputChannel, jobject inputChannelObj) { + jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj); + if (! inputChannelObjWeak) { + LOGE("Could not create weak reference for input channel."); + LOGE_EX(env); + return NO_MEMORY; + } + + status_t status; + { + AutoMutex _l(mInputChannelRegistryLock); + + ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey( + inputChannel->getReceivePipeFd()); + if (index >= 0) { + LOGE("Input channel object '%s' has already been registered", + inputChannel->getName().string()); + status = INVALID_OPERATION; + goto DeleteWeakRef; + } + + mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(), + inputChannelObjWeak); + } + + status = mInputManager->registerInputChannel(inputChannel); + if (! status) { + return OK; + } + + { + AutoMutex _l(mInputChannelRegistryLock); + mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd()); + } + +DeleteWeakRef: + env->DeleteWeakGlobalRef(inputChannelObjWeak); + return status; +} + +status_t NativeInputManager::unregisterInputChannel(JNIEnv* env, + const sp<InputChannel>& inputChannel) { + jweak inputChannelObjWeak; + { + AutoMutex _l(mInputChannelRegistryLock); + + ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey( + inputChannel->getReceivePipeFd()); + if (index < 0) { + LOGE("Input channel object '%s' is not currently registered", + inputChannel->getName().string()); + return INVALID_OPERATION; + } + + inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index); + mInputChannelObjWeakByReceiveFd.removeItemsAt(index); + } + + env->DeleteWeakGlobalRef(inputChannelObjWeak); + + return mInputManager->unregisterInputChannel(inputChannel); +} + +jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env, + const sp<InputChannel>& inputChannel) { + { + AutoMutex _l(mInputChannelRegistryLock); + + ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey( + inputChannel->getReceivePipeFd()); + if (index < 0) { + return NULL; + } + + jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index); + return env->NewLocalRef(inputChannelObjWeak); + } +} + bool NativeInputManager::getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation) { bool result = false; @@ -216,7 +326,7 @@ bool NativeInputManager::isScreenOn() { JNIEnv* env = jniEnv(); jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn); - if (checkExceptionFromCallback(env, "isScreenOn")) { + if (checkAndClearExceptionFromCallback(env, "isScreenOn")) { return true; } return result; @@ -226,7 +336,7 @@ bool NativeInputManager::isScreenBright() { JNIEnv* env = jniEnv(); jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright); - if (checkExceptionFromCallback(env, "isScreenBright")) { + if (checkAndClearExceptionFromCallback(env, "isScreenBright")) { return true; } return result; @@ -245,7 +355,7 @@ void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId, env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback, when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); - checkExceptionFromCallback(env, "virtualKeyFeedback"); + checkAndClearExceptionFromCallback(env, "virtualKeyFeedback"); } int32_t NativeInputManager::interceptKey(nsecs_t when, @@ -267,7 +377,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey, deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn); - if (checkExceptionFromCallback(env, "hackInterceptKey")) { + if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) { wmActions = 0; } @@ -284,22 +394,20 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, if (wmActions & WM_ACTION_GO_TO_SLEEP) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when); - checkExceptionFromCallback(env, "goToSleep"); + checkAndClearExceptionFromCallback(env, "goToSleep"); } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when); - checkExceptionFromCallback(env, "pokeUserActivityForKey"); + checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey"); } if (wmActions & WM_ACTION_PASS_TO_USER) { actions |= InputReaderPolicyInterface::ACTION_DISPATCH; - } - if (! (wmActions & WM_ACTION_PASS_TO_USER)) { if (down && isAppSwitchKey(keyCode)) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing); - checkExceptionFromCallback(env, "notifyAppSwitchComing"); + checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing"); actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING; } @@ -312,12 +420,18 @@ int32_t NativeInputManager::interceptTouch(nsecs_t when) { LOGD("interceptTouch - when=%lld", when); #endif - if (! isScreenOn()) { - // Touch events do not wake the device. - return InputReaderPolicyInterface::ACTION_NONE; + int32_t actions = InputReaderPolicyInterface::ACTION_NONE; + if (isScreenOn()) { + // Only dispatch touch events when the device is awake. + actions |= InputReaderPolicyInterface::ACTION_DISPATCH; + } + + if (! isScreenBright()) { + // Brighten the screen if dimmed. + actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; } - return InputReaderPolicyInterface::ACTION_DISPATCH; + return actions; } int32_t NativeInputManager::interceptTrackball(nsecs_t when, @@ -327,12 +441,18 @@ int32_t NativeInputManager::interceptTrackball(nsecs_t when, when, buttonChanged, buttonDown, rolled); #endif - if (! isScreenOn()) { - // Trackball motions and button presses do not wake the device. - return InputReaderPolicyInterface::ACTION_NONE; + int32_t actions = InputReaderPolicyInterface::ACTION_NONE; + if (isScreenOn()) { + // Only dispatch trackball events when the device is awake. + actions |= InputReaderPolicyInterface::ACTION_DISPATCH; + } + + if (! isScreenBright()) { + // Brighten the screen if dimmed. + actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; } - return InputReaderPolicyInterface::ACTION_DISPATCH; + return actions; } int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode, @@ -348,7 +468,7 @@ int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode, case SW_LID: env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, when, switchValue == 0); - checkExceptionFromCallback(env, "notifyLidSwitchChanged"); + checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged"); break; } @@ -361,7 +481,7 @@ bool NativeInputManager::filterTouchEvents() { jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterTouchEvents); - if (checkExceptionFromCallback(env, "filterTouchEvents")) { + if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) { result = false; } @@ -376,7 +496,7 @@ bool NativeInputManager::filterJumpyTouchEvents() { jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterJumpyTouchEvents); - if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) { + if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) { result = false; } @@ -390,10 +510,10 @@ void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, JNIEnv* env = jniEnv(); jstring deviceNameStr = env->NewStringUTF(deviceName.string()); - if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) { + if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) { jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr)); - if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) { + if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) { jsize length = env->GetArrayLength(result); for (jsize i = 0; i < length; i++) { jobject item = env->GetObjectArrayElement(result, i); @@ -423,7 +543,7 @@ void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDevi jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getExcludedDeviceNames)); - if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) { + if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) { jsize length = env->GetArrayLength(result); for (jsize i = 0; i < length; i++) { jstring item = jstring(env->GetObjectArrayElement(result, i)); @@ -450,7 +570,7 @@ void NativeInputManager::notifyConfigurationChanged(nsecs_t when) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged, when, config.touchScreen, config.keyboard, config.navigation); - checkExceptionFromCallback(env, "notifyConfigurationChanged"); + checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged"); } void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) { @@ -458,16 +578,47 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputC LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string()); #endif - // TODO + JNIEnv* env = jniEnv(); + + jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); + if (inputChannelObjLocal) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken, + inputChannelObjLocal); + checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken"); + + env->DeleteLocalRef(inputChannelObjLocal); + } } -void NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) { +bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel, + nsecs_t& outNewTimeout) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyInputChannelANR - inputChannel='%s'", inputChannel->getName().string()); #endif - // TODO + JNIEnv* env = jniEnv(); + + jlong newTimeout; + jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); + if (inputChannelObjLocal) { + newTimeout = env->CallLongMethod(mCallbacksObj, + gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal); + if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) { + newTimeout = -2; + } + + env->DeleteLocalRef(inputChannelObjLocal); + } else { + newTimeout = -2; + } + + if (newTimeout == -2) { + return false; // abort + } + + outNewTimeout = newTimeout; + return true; // resume } void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) { @@ -476,7 +627,16 @@ void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChanne inputChannel->getName().string()); #endif - // TODO + JNIEnv* env = jniEnv(); + + jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); + if (inputChannelObjLocal) { + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, + inputChannelObjLocal); + checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR"); + + env->DeleteLocalRef(inputChannelObjLocal); + } } nsecs_t NativeInputManager::getKeyRepeatTimeout() { @@ -489,73 +649,83 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() { } } -void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets) { +int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("getKeyEventTargets - policyFlags=%d", policyFlags); + LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + policyFlags, injectorPid, injectorUid); #endif JNIEnv* env = jniEnv(); + jint injectionResult; jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); if (! keyEventObj) { LOGE("Could not obtain DVM KeyEvent object to get key event targets."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; } else { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, - gCallbacksClassInfo.getKeyEventTargets, - keyEventObj, jint(keyEvent->getNature()), jint(policyFlags))); - if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - if (! item) { - break; // found null element indicating end of used portion of the array - } - - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); + jint injectionResult = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj, + keyEventObj, jint(keyEvent->getNature()), jint(policyFlags), + jint(injectorPid), jint(injectorUid)); + if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) { + injectionResult = INPUT_EVENT_INJECTION_FAILED; + } else { + populateInputTargets(env, mReusableInputTargetListObj, outTargets); } env->DeleteLocalRef(keyEventObj); } + return injectionResult; } -void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, - Vector<InputTarget>& outTargets) { +int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("getMotionEventTargets - policyFlags=%d", policyFlags); + LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + policyFlags, injectorPid, injectorUid); #endif JNIEnv* env = jniEnv(); + jint injectionResult; jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent); if (! motionEventObj) { LOGE("Could not obtain DVM MotionEvent object to get key event targets."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; } else { - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, - gCallbacksClassInfo.getMotionEventTargets, - motionEventObj, jint(motionEvent->getNature()), jint(policyFlags))); - if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) { - jsize length = env->GetArrayLength(result); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(result, i); - if (! item) { - break; // found null element indicating end of used portion of the array - } - - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); - - env->DeleteLocalRef(item); - } - env->DeleteLocalRef(result); + jint injectionResult = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj, + motionEventObj, jint(motionEvent->getNature()), jint(policyFlags), + jint(injectorPid), jint(injectorUid)); + if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) { + injectionResult = INPUT_EVENT_INJECTION_FAILED; + } else { + populateInputTargets(env, mReusableInputTargetListObj, outTargets); } android_view_MotionEvent_recycle(env, motionEventObj); env->DeleteLocalRef(motionEventObj); } + return injectionResult; +} + +void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj, + Vector<InputTarget>& outTargets) { + jobjectArray inputTargetArray = jobjectArray(env->GetObjectField( + inputTargetListObj, gInputTargetListClassInfo.mArray)); + + jsize length = env->GetArrayLength(inputTargetArray); + for (jsize i = 0; i < length; i++) { + jobject item = env->GetObjectArrayElement(inputTargetArray, i); + if (! item) { + break; // found null element indicating end of used portion of the array + } + + outTargets.add(); + android_view_InputTarget_toNative(env, item, & outTargets.editTop()); + + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(inputTargetArray); } @@ -676,7 +846,7 @@ static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env, "the input manager!", inputChannel->getName().string()); if (gNativeInputManager != NULL) { - gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel); + gNativeInputManager->unregisterInputChannel(env, inputChannel); } } @@ -693,7 +863,9 @@ static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, return; } - status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel); + + status_t status = gNativeInputManager->registerInputChannel( + env, inputChannel, inputChannelObj); if (status) { jniThrowRuntimeException(env, "Failed to register input channel. " "Check logs for details."); @@ -719,13 +891,41 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); - status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel); + status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel); if (status) { jniThrowRuntimeException(env, "Failed to unregister input channel. " "Check logs for details."); } } +static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz, + jobject keyEventObj, jint nature, jint injectorPid, jint injectorUid, + jboolean sync, jint timeoutMillis) { + if (checkInputManagerUnitialized(env)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + KeyEvent keyEvent; + android_view_KeyEvent_toNative(env, keyEventObj, nature, & keyEvent); + + return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, + injectorPid, injectorUid, sync, timeoutMillis); +} + +static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz, + jobject motionEventObj, jint nature, jint injectorPid, jint injectorUid, + jboolean sync, jint timeoutMillis) { + if (checkInputManagerUnitialized(env)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + MotionEvent motionEvent; + android_view_MotionEvent_toNative(env, motionEventObj, nature, & motionEvent); + + return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, + injectorPid, injectorUid, sync, timeoutMillis); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gInputManagerMethods[] = { @@ -749,7 +949,11 @@ static JNINativeMethod gInputManagerMethods[] = { { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", - (void*) android_server_InputManager_nativeUnregisterInputChannel } + (void*) android_server_InputManager_nativeUnregisterInputChannel }, + { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I", + (void*) android_server_InputManager_nativeInjectKeyEvent }, + { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I", + (void*) android_server_InputManager_nativeInjectMotionEvent } }; #define FIND_CLASS(var, className) \ @@ -786,6 +990,15 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz, "notifyLidSwitchChanged", "(JZ)V"); + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, + "notifyInputChannelBroken", "(Landroid/view/InputChannel;)V"); + + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz, + "notifyInputChannelANR", "(Landroid/view/InputChannel;)J"); + + GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz, + "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V"); + GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz, "virtualKeyFeedback", "(JIIIIIIJ)V"); @@ -815,10 +1028,12 @@ int register_android_server_InputManager(JNIEnv* env) { "getExcludedDeviceNames", "()[Ljava/lang/String;"); GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz, - "getKeyEventTargets", "(Landroid/view/KeyEvent;II)[Landroid/view/InputTarget;"); + "getKeyEventTargets", + "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I"); GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz, - "getMotionEventTargets", "(Landroid/view/MotionEvent;II)[Landroid/view/InputTarget;"); + "getMotionEventTargets", + "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I"); // VirtualKeyDefinition @@ -840,6 +1055,16 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz, "height", "I"); + // InputTargetList + + FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList"); + + GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz, + "<init>", "()V"); + + GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz, + "mArray", "[Landroid/view/InputTarget;"); + return 0; } diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp index afd6ca8..0b41dd8 100755 --- a/services/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp @@ -25,12 +25,13 @@ #include "hardware_legacy/power.h" #include "utils/Log.h" #include "utils/misc.h" +#include "android_runtime/AndroidRuntime.h" #include <string.h> #include <pthread.h> -static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER; +static jobject mCallbacksObj = NULL; + static jmethodID method_reportLocation; static jmethodID method_reportStatus; static jmethodID method_reportSvStatus; @@ -46,140 +47,89 @@ static const AGpsInterface* sAGpsInterface = NULL; static const GpsNiInterface* sGpsNiInterface = NULL; static const GpsDebugInterface* sGpsDebugInterface = NULL; -// data written to by GPS callbacks -static GpsLocation sGpsLocation; -static GpsStatus sGpsStatus; +// temporary storage for GPS callbacks static GpsSvStatus sGpsSvStatus; -static AGpsStatus sAGpsStatus; -static GpsNiNotification sGpsNiNotification; +static const char* sNmeaString; +static int sNmeaStringLength; #define WAKE_LOCK_NAME "GPS" -// buffer for NMEA data -#define NMEA_SENTENCE_LENGTH 100 -#define NMEA_SENTENCE_COUNT 40 -struct NmeaSentence { - GpsUtcTime timestamp; - char nmea[NMEA_SENTENCE_LENGTH]; -}; -static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_COUNT]; -static int mNmeaSentenceCount = 0; - -// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event -// and android_location_GpsLocationProvider_read_status -static GpsLocation sGpsLocationCopy; -static GpsStatus sGpsStatusCopy; -static GpsSvStatus sGpsSvStatusCopy; -static AGpsStatus sAGpsStatusCopy; -static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_COUNT]; -static GpsNiNotification sGpsNiNotificationCopy; -static uint32_t sEngineCapabilities; - - -enum CallbackType { - kLocation = 1, - kStatus = 2, - kSvStatus = 4, - kAGpsStatus = 8, - kXtraDownloadRequest = 16, - kDisableRequest = 32, - kNmeaAvailable = 64, - kNiNotification = 128, - kSetCapabilities = 256, -}; -static int sPendingCallbacks; - namespace android { +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + static void location_callback(GpsLocation* location) { - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kLocation; - memcpy(&sGpsLocation, location, sizeof(sGpsLocation)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("location_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags, + (jdouble)location->latitude, (jdouble)location->longitude, + (jdouble)location->altitude, + (jfloat)location->speed, (jfloat)location->bearing, + (jfloat)location->accuracy, (jlong)location->timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); } static void status_callback(GpsStatus* status) { - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kStatus; - memcpy(&sGpsStatus, status, sizeof(sGpsStatus)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("status_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + LOGD("env: %p obj: %p\n", env, mCallbacksObj); + env->CallVoidMethod(mCallbacksObj, method_reportStatus, status->status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); } static void sv_status_callback(GpsSvStatus* sv_status) { - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kSvStatus; - memcpy(&sGpsSvStatus, sv_status, sizeof(GpsSvStatus)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("sv_status_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + memcpy(&sGpsSvStatus, sv_status, sizeof(sGpsSvStatus)); + env->CallVoidMethod(mCallbacksObj, method_reportSvStatus); + checkAndClearExceptionFromCallback(env, __FUNCTION__); } static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) { - pthread_mutex_lock(&sEventMutex); - - if (length >= NMEA_SENTENCE_LENGTH) { - LOGE("NMEA data too long in nmea_callback (length = %d)\n", length); - length = NMEA_SENTENCE_LENGTH - 1; - } - if (mNmeaSentenceCount >= NMEA_SENTENCE_COUNT) { - LOGE("NMEA data overflowed buffer\n"); - pthread_mutex_unlock(&sEventMutex); - return; - } - - sPendingCallbacks |= kNmeaAvailable; - sNmeaBuffer[mNmeaSentenceCount].timestamp = timestamp; - memcpy(sNmeaBuffer[mNmeaSentenceCount].nmea, nmea, length); - sNmeaBuffer[mNmeaSentenceCount].nmea[length] = 0; - mNmeaSentenceCount++; - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("nmea_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + // The Java code will call back to read these values + // We do this to avoid creating unnecessary String objects + sNmeaString = nmea; + sNmeaStringLength = length; + env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); } static void set_capabilities_callback(uint32_t capabilities) { - LOGD("set_capabilities_callback: %08X", capabilities); - - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kSetCapabilities; - sEngineCapabilities = capabilities; - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("set_capabilities_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities); + checkAndClearExceptionFromCallback(env, __FUNCTION__); } static void acquire_wakelock_callback() { + LOGD("acquire_wakelock_callback\n"); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); } static void release_wakelock_callback() { + LOGD("release_wakelock_callback\n"); release_wake_lock(WAKE_LOCK_NAME); } -static void agps_status_callback(AGpsStatus* agps_status) +static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg) { - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kAGpsStatus; - memcpy(&sAGpsStatus, agps_status, sizeof(AGpsStatus)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("create_thread_callback\n"); + return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg); } GpsCallbacks sGpsCallbacks = { @@ -191,41 +141,67 @@ GpsCallbacks sGpsCallbacks = { set_capabilities_callback, acquire_wakelock_callback, release_wakelock_callback, + create_thread_callback, }; -static void -download_request_callback() +static void xtra_download_request_callback() { - pthread_mutex_lock(&sEventMutex); - sPendingCallbacks |= kXtraDownloadRequest; - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - -static void -gps_ni_notify_callback(GpsNiNotification *notification) -{ - LOGD("gps_ni_notify_callback: notif=%d", notification->notification_id); - - pthread_mutex_lock(&sEventMutex); - - sPendingCallbacks |= kNiNotification; - memcpy(&sGpsNiNotification, notification, sizeof(GpsNiNotification)); - - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); + LOGD("xtra_download_request_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest); + checkAndClearExceptionFromCallback(env, __FUNCTION__); } GpsXtraCallbacks sGpsXtraCallbacks = { - download_request_callback, + xtra_download_request_callback, + create_thread_callback, }; +static void agps_status_callback(AGpsStatus* agps_status) +{ + LOGD("agps_status_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, + agps_status->type, agps_status->status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + AGpsCallbacks sAGpsCallbacks = { agps_status_callback, + create_thread_callback, }; +static void gps_ni_notify_callback(GpsNiNotification *notification) +{ + LOGD("gps_ni_notify_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jstring requestor_id = env->NewStringUTF(notification->requestor_id); + jstring text = env->NewStringUTF(notification->text); + jstring extras = env->NewStringUTF(notification->extras); + + if (requestor_id && text && extras) { + env->CallVoidMethod(mCallbacksObj, method_reportNiNotification, + notification->notification_id, notification->ni_type, + notification->notify_flags, notification->timeout, + notification->default_response, requestor_id, text, + notification->requestor_id_encoding, + notification->text_encoding, extras); + } else { + LOGE("out of memory in gps_ni_notify_callback\n"); + } + + if (requestor_id) + env->DeleteLocalRef(requestor_id); + if (text) + env->DeleteLocalRef(text); + if (extras) + env->DeleteLocalRef(extras); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + GpsNiCallbacks sGpsNiCallbacks = { gps_ni_notify_callback, + create_thread_callback, }; static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { @@ -233,7 +209,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V"); - method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V"); + method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V"); method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V"); method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); @@ -265,6 +241,12 @@ static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, j static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) { + LOGD("android_location_GpsLocationProvider_init obj: %p\n", obj); + + // this must be set before calling into the HAL library + if (!mCallbacksObj) + mCallbacksObj = env->NewGlobalRef(obj); + if (!sGpsInterface) sGpsInterface = get_gps_interface(); if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) @@ -286,14 +268,6 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o return true; } -static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj) -{ - pthread_mutex_lock(&sEventMutex); - sPendingCallbacks |= kDisableRequest; - pthread_cond_signal(&sEventCond); - pthread_mutex_unlock(&sEventMutex); -} - static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj) { sGpsInterface->cleanup(); @@ -321,90 +295,11 @@ static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, sGpsInterface->delete_aiding_data(flags); } -static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj) -{ - pthread_mutex_lock(&sEventMutex); - while (sPendingCallbacks == 0) { - pthread_cond_wait(&sEventCond, &sEventMutex); - } - - // copy and clear the callback flags - int pendingCallbacks = sPendingCallbacks; - sPendingCallbacks = 0; - int nmeaSentenceCount = mNmeaSentenceCount; - mNmeaSentenceCount = 0; - - // copy everything and unlock the mutex before calling into Java code to avoid the possibility - // of timeouts in the GPS engine. - if (pendingCallbacks & kLocation) - memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy)); - if (pendingCallbacks & kStatus) - memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy)); - if (pendingCallbacks & kSvStatus) - memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy)); - if (pendingCallbacks & kAGpsStatus) - memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy)); - if (pendingCallbacks & kNmeaAvailable) - memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0])); - if (pendingCallbacks & kNiNotification) - memcpy(&sGpsNiNotificationCopy, &sGpsNiNotification, sizeof(sGpsNiNotificationCopy)); - pthread_mutex_unlock(&sEventMutex); - - if (pendingCallbacks & kLocation) { - env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags, - (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude, - (jdouble)sGpsLocationCopy.altitude, - (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing, - (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp); - } - if (pendingCallbacks & kStatus) { - env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status); - } - if (pendingCallbacks & kSvStatus) { - env->CallVoidMethod(obj, method_reportSvStatus); - } - if (pendingCallbacks & kAGpsStatus) { - env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status); - } - if (pendingCallbacks & kNmeaAvailable) { - for (int i = 0; i < nmeaSentenceCount; i++) { - env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp); - } - } - if (pendingCallbacks & kXtraDownloadRequest) { - env->CallVoidMethod(obj, method_xtraDownloadRequest); - } - if (pendingCallbacks & kDisableRequest) { - // don't need to do anything - we are just poking so wait_for_event will return. - } - if (pendingCallbacks & kNiNotification) { - LOGD("android_location_GpsLocationProvider_wait_for_event: sent notification callback."); - jstring reqId = env->NewStringUTF(sGpsNiNotificationCopy.requestor_id); - jstring text = env->NewStringUTF(sGpsNiNotificationCopy.text); - jstring extras = env->NewStringUTF(sGpsNiNotificationCopy.extras); - env->CallVoidMethod(obj, method_reportNiNotification, - sGpsNiNotificationCopy.notification_id, - sGpsNiNotificationCopy.ni_type, - sGpsNiNotificationCopy.notify_flags, - sGpsNiNotificationCopy.timeout, - sGpsNiNotificationCopy.default_response, - reqId, - text, - sGpsNiNotificationCopy.requestor_id_encoding, - sGpsNiNotificationCopy.text_encoding, - extras - ); - } - if (pendingCallbacks & kSetCapabilities) { - env->CallVoidMethod(obj, method_setEngineCapabilities, sEngineCapabilities); - } -} - static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, jintArray maskArray) { - // this should only be called from within a call to reportStatus, so we don't need to lock here + // this should only be called from within a call to reportSvStatus jint* prns = env->GetIntArrayElements(prnArray, 0); jfloat* snrs = env->GetFloatArrayElements(snrArray, 0); @@ -412,16 +307,16 @@ static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, job jfloat* azim = env->GetFloatArrayElements(azumArray, 0); jint* mask = env->GetIntArrayElements(maskArray, 0); - int num_svs = sGpsSvStatusCopy.num_svs; + int num_svs = sGpsSvStatus.num_svs; for (int i = 0; i < num_svs; i++) { - prns[i] = sGpsSvStatusCopy.sv_list[i].prn; - snrs[i] = sGpsSvStatusCopy.sv_list[i].snr; - elev[i] = sGpsSvStatusCopy.sv_list[i].elevation; - azim[i] = sGpsSvStatusCopy.sv_list[i].azimuth; + prns[i] = sGpsSvStatus.sv_list[i].prn; + snrs[i] = sGpsSvStatus.sv_list[i].snr; + elev[i] = sGpsSvStatus.sv_list[i].elevation; + azim[i] = sGpsSvStatus.sv_list[i].azimuth; } - mask[0] = sGpsSvStatusCopy.ephemeris_mask; - mask[1] = sGpsSvStatusCopy.almanac_mask; - mask[2] = sGpsSvStatusCopy.used_in_fix_mask; + mask[0] = sGpsSvStatus.ephemeris_mask; + mask[1] = sGpsSvStatus.almanac_mask; + mask[2] = sGpsSvStatus.used_in_fix_mask; env->ReleaseIntArrayElements(prnArray, prns, 0); env->ReleaseFloatArrayElements(snrArray, snrs, 0); @@ -431,23 +326,21 @@ static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, job return num_svs; } -static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jint index, jbyteArray nmeaArray, jint buffer_size) +static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, + jbyteArray nmeaArray, jint buffer_size) { - // this should only be called from within a call to reportNmea, so we don't need to lock here - - jbyte* nmea = env->GetByteArrayElements(nmeaArray, 0); - - int length = strlen(sNmeaBufferCopy[index].nmea); + // this should only be called from within a call to reportNmea + jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0); + int length = sNmeaStringLength; if (length > buffer_size) length = buffer_size; - memcpy(nmea, sNmeaBufferCopy[index].nmea, length); - - env->ReleaseByteArrayElements(nmeaArray, nmea, 0); + memcpy(nmea, sNmeaString, length); + env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT); return length; } -static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time, - jlong timeReference, jint uncertainty) +static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, + jlong time, jlong timeReference, jint uncertainty) { sGpsInterface->inject_time(time, timeReference, uncertainty); } @@ -476,9 +369,9 @@ static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, jbyteArray data, jint length) { - jbyte* bytes = env->GetByteArrayElements(data, 0); + jbyte* bytes = (jbyte *)env->GetPrimitiveArrayCritical(data, 0); sGpsXtraInterface->inject_xtra_data((char *)bytes, length); - env->ReleaseByteArrayElements(data, bytes, 0); + env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); } static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn) @@ -560,15 +453,13 @@ static JNINativeMethod sMethods[] = { {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported}, {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}, - {"native_disable", "()V", (void*)android_location_GpsLocationProvider_disable}, {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup}, {"native_set_position_mode", "(IIIII)Z", (void*)android_location_GpsLocationProvider_set_position_mode}, {"native_start", "()Z", (void*)android_location_GpsLocationProvider_start}, {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop}, {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, - {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, - {"native_read_nmea", "(I[BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, + {"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, |
