/* * Copyright (C) 2013-2017 microG Project Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.microg.gms.common; import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class MultiConnectionKeeper { private static final String TAG = "GmsMultiConKeeper"; private static MultiConnectionKeeper INSTANCE; private final Context context; private final Map connections = new HashMap<>(); public MultiConnectionKeeper(Context context) { this.context = context; } public synchronized static MultiConnectionKeeper getInstance(Context context) { if (INSTANCE == null) INSTANCE = new MultiConnectionKeeper(context); return INSTANCE; } public synchronized boolean bind(String action, ServiceConnection connection) { Log.d(TAG, "bind(" + action + ", " + connection + ")"); Connection con = connections.get(action); if (con != null) { if (!con.forwardsConnection(connection)) { con.addConnectionForward(connection); if (!con.isBound()) con.bind(); } } else { con = new Connection(action); con.addConnectionForward(connection); con.bind(); connections.put(action, con); } return con.isBound(); } public synchronized void unbind(String action, ServiceConnection connection) { Log.d(TAG, "unbind(" + action + ", " + connection + ")"); Connection con = connections.get(action); if (con != null) { con.removeConnectionForward(connection); if (!con.hasForwards() && con.isBound()) { con.unbind(); connections.remove(action); } } } public class Connection { private final String actionString; private final Set connectionForwards = new HashSet<>(); private boolean bound = false; private boolean connected = false; private IBinder binder; private ComponentName component; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + "onServiceConnected(" + componentName + ")"); binder = iBinder; component = componentName; for (ServiceConnection connection : connectionForwards) { connection.onServiceConnected(componentName, iBinder); } connected = true; } @Override public void onServiceDisconnected(ComponentName componentName) { Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + "onServiceDisconnected(" + componentName + ")"); binder = null; component = componentName; for (ServiceConnection connection : connectionForwards) { connection.onServiceDisconnected(componentName); } connected = false; bound = false; } }; public Connection(String actionString) { this.actionString = actionString; } @SuppressLint("InlinedApi") public void bind() { Log.d(TAG, "Connection(" + actionString + ") : bind()"); Intent gmsIntent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); Intent selfIntent = new Intent(actionString).setPackage(context.getPackageName()); Intent intent; if (context.getPackageManager().resolveService(gmsIntent, 0) == null) { Log.w(TAG, "No GMS service found for " + actionString); if (context.getPackageManager().resolveService(selfIntent, 0) != null) { Log.d(TAG, "Found service for "+actionString+" in self package, using it instead"); intent = selfIntent; } else { return; } } else { intent = gmsIntent; } int flags = Context.BIND_AUTO_CREATE; if (SDK_INT >= ICE_CREAM_SANDWICH) { flags |= Context.BIND_ADJUST_WITH_ACTIVITY; } bound = context.bindService(intent, serviceConnection, flags); if (!bound) { context.unbindService(serviceConnection); } } public boolean isBound() { return bound; } public IBinder getBinder() { return binder; } public void unbind() { Log.d(TAG, "Connection(" + actionString + ") : unbind()"); try { context.unbindService(serviceConnection); } catch (IllegalArgumentException e) { // not bound (whatever reason) Log.w(TAG, e); } bound = false; } public void addConnectionForward(ServiceConnection connection) { connectionForwards.add(connection); if (connected) { connection.onServiceConnected(component, binder); } } public void removeConnectionForward(ServiceConnection connection) { connectionForwards.remove(connection); if (connected) { connection.onServiceDisconnected(component); } } public boolean forwardsConnection(ServiceConnection connection) { return connectionForwards.contains(connection); } public boolean hasForwards() { return !connectionForwards.isEmpty(); } } }