1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.car.pm;
17
18import android.car.content.pm.CarAppBlockingPolicy;
19import android.car.content.pm.ICarAppBlockingPolicy;
20import android.car.content.pm.ICarAppBlockingPolicySetter;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.content.pm.ServiceInfo;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.RemoteException;
29import android.os.UserHandle;
30import android.util.Log;
31
32import com.android.car.CarLog;
33import com.android.internal.annotations.GuardedBy;
34
35public class AppBlockingPolicyProxy implements ServiceConnection {
36
37    private final CarPackageManagerService mService;
38    private final Context mContext;
39    private final ServiceInfo mServiceInfo;
40    private final ICarAppBlockingPolicySetterImpl mSetter;
41
42    @GuardedBy("this")
43    private ICarAppBlockingPolicy mPolicyService = null;
44
45    /**
46     * policy not set within this time after binding will be treated as failure and will be
47     * ignored.
48     */
49    private static final long TIMEOUT_MS = 5000;
50    private static final int MAX_CRASH_RETRY = 2;
51    @GuardedBy("this")
52    private int mCrashCount = 0;
53    @GuardedBy("this")
54    private boolean mBound = false;
55
56    private final Handler mHandler;
57    private final Runnable mTimeoutRunnable = new Runnable() {
58        @Override
59        public void run() {
60            Log.w(CarLog.TAG_PACKAGE, "Timeout for policy setting for service:" + mServiceInfo);
61            disconnect();
62            mService.onPolicyConnectionFailure(AppBlockingPolicyProxy.this);
63        }
64    };
65
66    public AppBlockingPolicyProxy(CarPackageManagerService service, Context context,
67            ServiceInfo serviceInfo) {
68        mService = service;
69        mContext = context;
70        mServiceInfo = serviceInfo;
71        mSetter = new ICarAppBlockingPolicySetterImpl();
72        mHandler = new Handler(mService.getLooper());
73    }
74
75    public String getPackageName() {
76        return mServiceInfo.packageName;
77    }
78
79    public void connect() {
80        Intent intent = new Intent();
81        intent.setClassName(mServiceInfo.packageName, mServiceInfo.name);
82        mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
83                UserHandle.SYSTEM);
84        synchronized (this) {
85            mBound = true;
86        }
87        mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
88    }
89
90    public void disconnect() {
91        synchronized (this) {
92            if (!mBound) {
93                return;
94            }
95            mBound = false;
96            mPolicyService = null;
97        }
98        mHandler.removeCallbacks(mTimeoutRunnable);
99        try {
100            mContext.unbindService(this);
101        } catch (IllegalArgumentException e) {
102            Log.w(CarLog.TAG_PACKAGE, "unbind", e);
103        }
104    }
105
106    @Override
107    public void onServiceConnected(ComponentName name, IBinder service) {
108        ICarAppBlockingPolicy policy = null;
109        boolean failed = false;
110        synchronized (this) {
111            mPolicyService = ICarAppBlockingPolicy.Stub.asInterface(service);
112            policy = mPolicyService;
113            if (policy == null) {
114                failed = true;
115            }
116        }
117        if (failed) {
118            Log.w(CarLog.TAG_PACKAGE, "Policy service connected with null binder:" + name);
119            mService.onPolicyConnectionFailure(this);
120            return;
121        }
122        try {
123            mPolicyService.setAppBlockingPolicySetter(mSetter);
124        } catch (RemoteException e) {
125            // let retry handle this
126        }
127    }
128
129    @Override
130    public void onServiceDisconnected(ComponentName name) {
131        boolean failed = false;
132        synchronized (this) {
133            mCrashCount++;
134            if (mCrashCount > MAX_CRASH_RETRY) {
135                mPolicyService = null;
136                failed = true;
137            }
138        }
139        if (failed) {
140            Log.w(CarLog.TAG_PACKAGE, "Policy service keep crashing, giving up:" + name);
141            mService.onPolicyConnectionFailure(this);
142        }
143    }
144
145    @Override
146    public String toString() {
147        return "AppBlockingPolicyProxy [mServiceInfo=" + mServiceInfo + ", mCrashCount="
148                + mCrashCount + "]";
149    }
150
151    private class ICarAppBlockingPolicySetterImpl extends ICarAppBlockingPolicySetter.Stub {
152
153        @Override
154        public void setAppBlockingPolicy(CarAppBlockingPolicy policy) {
155            mHandler.removeCallbacks(mTimeoutRunnable);
156            if (policy == null) {
157                Log.w(CarLog.TAG_PACKAGE, "setAppBlockingPolicy null policy from policy service:" +
158                        mServiceInfo);
159            }
160            mService.onPolicyConnectionAndSet(AppBlockingPolicyProxy.this, policy);
161        }
162    }
163}
164