// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.chrome.browser.gsa; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.chrome.browser.ChromeApplication; /** * A simple client that connects and talks to the GSAService using Messages. */ public class GSAServiceClient { private static final String TAG = "GSAServiceClient"; /** * Constants for gsa communication. These should not change without corresponding changes on the * service side in GSA. */ private static final String GSA_SERVICE = "com.google.android.ssb.action.SSB_SERVICE"; public static final int REQUEST_REGISTER_CLIENT = 2; public static final int RESPONSE_UPDATE_SSB = 3; public static final String KEY_GSA_STATE = "ssb_service:ssb_state"; public static final String KEY_GSA_CONTEXT = "ssb_service:ssb_context"; public static final String KEY_GSA_PACKAGE_NAME = "ssb_service:ssb_package_name"; /** Messenger to handle incoming messages from the service */ private final Messenger mMessenger; private final IncomingHandler mHandler; private final GSAServiceConnection mConnection; private final GSAHelper mGsaHelper; private Context mContext; /** Messenger for communicating with service. */ private Messenger mService; /** * Handler of incoming messages from service. */ @SuppressFBWarnings("BC_IMPOSSIBLE_CAST") @SuppressLint("HandlerLeak") private class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == RESPONSE_UPDATE_SSB) { if (mService == null) return; Bundle bundle = (Bundle) msg.obj; String account = mGsaHelper.getGSAAccountFromState(bundle.getByteArray(KEY_GSA_STATE)); GSAState.getInstance(mContext.getApplicationContext()).setGsaAccount(account); } else { super.handleMessage(msg); } } } /** * Constructs an instance of this class. */ public GSAServiceClient(Context context) { mHandler = new IncomingHandler(); mMessenger = new Messenger(mHandler); mContext = context; mConnection = new GSAServiceConnection(); mGsaHelper = ((ChromeApplication) mContext.getApplicationContext()) .createGsaHelper(); } /** * Establishes a connection to the service. Call this method once the callback passed here is * ready to handle calls. * If you pass in an GSA context, it will be sent up the service as soon as the connection is * established. * @return Whether or not the connection to the service was established successfully. */ public boolean connect() { if (mService != null) Log.e(TAG, "Already connected."); Intent intent = new Intent(GSA_SERVICE).setPackage(GSAState.SEARCH_INTENT_PACKAGE); return mContext.bindService( intent, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND); } /** * Disconnects from the service and resets the client's state. */ public void disconnect() { if (mService == null) return; mContext.unbindService(mConnection); mContext = null; mService = null; // Remove pending handler actions to prevent memory leaks. mHandler.removeCallbacksAndMessages(null); } /** * Indicates whether or not the client is currently connected to the service. * @return true if connected, false otherwise. */ public boolean isConnected() { return mService != null; } private class GSAServiceConnection implements ServiceConnection { private static final String SERVICE_CONNECTION_TAG = "GSAServiceConnection"; @Override public void onServiceDisconnected(ComponentName name) { mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { // Ignore this call if we disconnected in the meantime. if (mContext == null) return; mService = new Messenger(service); try { Message registerClientMessage = Message.obtain( null, REQUEST_REGISTER_CLIENT); registerClientMessage.replyTo = mMessenger; Bundle b = mGsaHelper.getBundleForRegisteringGSAClient(mContext); registerClientMessage.setData(b); registerClientMessage.getData().putString( KEY_GSA_PACKAGE_NAME, mContext.getPackageName()); mService.send(registerClientMessage); // Send prepare overlay message if there is a pending GSA context. } catch (RemoteException e) { Log.w(SERVICE_CONNECTION_TAG, "GSAServiceConnection - remote call failed", e); } } } }