1/*
2 * Copyright (C) 2011 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 */
16
17package com.android.settings.bluetooth;
18
19import android.bluetooth.BluetoothDevice;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.os.Bundle;
26import android.preference.Preference;
27import android.util.Log;
28import android.view.View;
29import android.widget.CheckBox;
30import android.widget.CompoundButton;
31import android.widget.EditText;
32import android.widget.TextView;
33import android.widget.Button;
34import android.widget.CompoundButton.OnCheckedChangeListener;
35
36import com.android.internal.app.AlertActivity;
37import com.android.internal.app.AlertController;
38
39import com.android.settings.R;
40
41/**
42 * BluetoothPermissionActivity shows a dialog for accepting incoming
43 * profile connection request from untrusted devices.
44 * It is also used to show a dialogue for accepting incoming phonebook
45 * read request. The request could be initiated by PBAP PCE or by HF AT+CPBR.
46 */
47public class BluetoothPermissionActivity extends AlertActivity implements
48        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
49    private static final String TAG = "BluetoothPermissionActivity";
50    private static final boolean DEBUG = Utils.D;
51
52    private View mView;
53    private TextView messageView;
54    private Button mOkButton;
55    private BluetoothDevice mDevice;
56    private String mReturnPackage = null;
57    private String mReturnClass = null;
58
59    private CheckBox mRememberChoice;
60    private boolean mRememberChoiceValue = false;
61
62    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
63        @Override
64        public void onReceive(Context context, Intent intent) {
65            String action = intent.getAction();
66            if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
67                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
68                if (mDevice.equals(device)) dismissDialog();
69            }
70        }
71    };
72    private boolean mReceiverRegistered = false;
73
74    private void dismissDialog() {
75        this.dismiss();
76    }
77
78    @Override
79    protected void onCreate(Bundle savedInstanceState) {
80        super.onCreate(savedInstanceState);
81
82        Intent i = getIntent();
83        String action = i.getAction();
84        if (!action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
85            Log.e(TAG, "Error: this activity may be started only with intent "
86                  + "ACTION_CONNECTION_ACCESS_REQUEST");
87            finish();
88            return;
89        }
90
91        mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
92        mReturnPackage = i.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME);
93        mReturnClass = i.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME);
94        int requestType = i.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
95                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
96
97        if (requestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) {
98            showConnectionDialog();
99        } else if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
100            showPhonebookDialog();
101        } else {
102            Log.e(TAG, "Error: bad request type: " + requestType);
103            finish();
104            return;
105        }
106        registerReceiver(mReceiver,
107                         new IntentFilter(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL));
108        mReceiverRegistered = true;
109    }
110
111    private void showConnectionDialog() {
112        final AlertController.AlertParams p = mAlertParams;
113        p.mIconId = android.R.drawable.ic_dialog_info;
114        p.mTitle = getString(R.string.bluetooth_connection_permission_request);
115        p.mView = createConnectionDialogView();
116        p.mPositiveButtonText = getString(R.string.yes);
117        p.mPositiveButtonListener = this;
118        p.mNegativeButtonText = getString(R.string.no);
119        p.mNegativeButtonListener = this;
120        mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
121        setupAlert();
122    }
123
124    private void showPhonebookDialog() {
125        final AlertController.AlertParams p = mAlertParams;
126        p.mIconId = android.R.drawable.ic_dialog_info;
127        p.mTitle = getString(R.string.bluetooth_phonebook_request);
128        p.mView = createPhonebookDialogView();
129        p.mPositiveButtonText = getString(android.R.string.yes);
130        p.mPositiveButtonListener = this;
131        p.mNegativeButtonText = getString(android.R.string.no);
132        p.mNegativeButtonListener = this;
133        mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
134        setupAlert();
135    }
136
137    private String createConnectionDisplayText() {
138        String mRemoteName = mDevice != null ? mDevice.getAliasName() : null;
139
140        if (mRemoteName == null) mRemoteName = getString(R.string.unknown);
141        String mMessage1 = getString(R.string.bluetooth_connection_dialog_text,
142                mRemoteName);
143        return mMessage1;
144    }
145
146    private String createPhonebookDisplayText() {
147        String mRemoteName = mDevice != null ? mDevice.getAliasName() : null;
148
149        if (mRemoteName == null) mRemoteName = getString(R.string.unknown);
150        String mMessage1 = getString(R.string.bluetooth_pb_acceptance_dialog_text,
151                                     mRemoteName, mRemoteName);
152        return mMessage1;
153    }
154
155    private View createConnectionDialogView() {
156        mView = getLayoutInflater().inflate(R.layout.bluetooth_connection_access, null);
157        messageView = (TextView)mView.findViewById(R.id.message);
158        messageView.setText(createConnectionDisplayText());
159        return mView;
160    }
161
162    private View createPhonebookDialogView() {
163        mView = getLayoutInflater().inflate(R.layout.bluetooth_pb_access, null);
164        messageView = (TextView)mView.findViewById(R.id.message);
165        messageView.setText(createPhonebookDisplayText());
166        mRememberChoice = (CheckBox)mView.findViewById(R.id.bluetooth_pb_remember_choice);
167        mRememberChoice.setChecked(false);
168        mRememberChoice.setOnCheckedChangeListener(new OnCheckedChangeListener() {
169            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
170                if (isChecked) {
171                    mRememberChoiceValue = true;
172                } else {
173                    mRememberChoiceValue = false;
174                }
175            }
176            });
177        return mView;
178    }
179
180    private void onPositive() {
181        if (DEBUG) Log.d(TAG, "onPositive mRememberChoiceValue: " + mRememberChoiceValue);
182
183        if (mRememberChoiceValue) {
184            savePhonebookPermissionChoice(CachedBluetoothDevice.PHONEBOOK_ACCESS_ALLOWED);
185        }
186        sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, true,
187                             BluetoothDevice.EXTRA_ALWAYS_ALLOWED, mRememberChoiceValue);
188        finish();
189    }
190
191    private void onNegative() {
192        if (DEBUG) Log.d(TAG, "onNegative mRememberChoiceValue: " + mRememberChoiceValue);
193
194        if (mRememberChoiceValue) {
195            savePhonebookPermissionChoice(CachedBluetoothDevice.PHONEBOOK_ACCESS_REJECTED);
196        }
197        sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, false,
198                             null, false // dummy value, no effect since last param is null
199                             );
200        finish();
201    }
202
203    private void sendIntentToReceiver(final String intentName, final boolean allowed,
204                                      final String extraName, final boolean extraValue) {
205        Intent intent = new Intent(intentName);
206
207        if (mReturnPackage != null && mReturnClass != null) {
208            intent.setClassName(mReturnPackage, mReturnClass);
209        }
210
211        intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
212                        allowed ? BluetoothDevice.CONNECTION_ACCESS_YES :
213                                  BluetoothDevice.CONNECTION_ACCESS_NO);
214
215        if (extraName != null) {
216            intent.putExtra(extraName, extraValue);
217        }
218        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
219        sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN);
220    }
221
222    public void onClick(DialogInterface dialog, int which) {
223        switch (which) {
224            case DialogInterface.BUTTON_POSITIVE:
225                onPositive();
226                break;
227
228            case DialogInterface.BUTTON_NEGATIVE:
229                onNegative();
230                break;
231            default:
232                break;
233        }
234    }
235
236    @Override
237    protected void onDestroy() {
238        super.onDestroy();
239        if (mReceiverRegistered) {
240            unregisterReceiver(mReceiver);
241            mReceiverRegistered = false;
242        }
243    }
244
245    public boolean onPreferenceChange(Preference preference, Object newValue) {
246        return true;
247    }
248
249    private void savePhonebookPermissionChoice(int permissionChoice) {
250        LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(this);
251        CachedBluetoothDeviceManager cachedDeviceManager =
252            bluetoothManager.getCachedDeviceManager();
253        CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice);
254        if (cachedDevice != null ) {
255            cachedDevice.setPhonebookPermissionChoice(permissionChoice);
256        } else {
257            cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(),
258                                                         bluetoothManager.getProfileManager(),
259                                                         mDevice);
260            cachedDevice.setPhonebookPermissionChoice(permissionChoice);
261        }
262    }
263}
264