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.TextView;
30import android.widget.Button;
31
32import com.android.internal.app.AlertActivity;
33import com.android.internal.app.AlertController;
34
35import com.android.settings.R;
36
37/**
38 * BluetoothPermissionActivity shows a dialog for accepting incoming
39 * profile connection request from untrusted devices.
40 * It is also used to show a dialogue for accepting incoming phonebook
41 * read request. The request could be initiated by PBAP PCE or by HF AT+CPBR.
42 */
43public class BluetoothPermissionActivity extends AlertActivity implements
44        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
45    private static final String TAG = "BluetoothPermissionActivity";
46    private static final boolean DEBUG = Utils.D;
47
48    private View mView;
49    private TextView messageView;
50    private Button mOkButton;
51    private BluetoothDevice mDevice;
52    private String mReturnPackage = null;
53    private String mReturnClass = null;
54
55    private int mRequestType = 0;
56    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
57        @Override
58        public void onReceive(Context context, Intent intent) {
59            String action = intent.getAction();
60            if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
61                int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
62                        BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
63                if (requestType != mRequestType) return;
64                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
65                if (mDevice.equals(device)) dismissDialog();
66            }
67        }
68    };
69    private boolean mReceiverRegistered = false;
70
71    private void dismissDialog() {
72        this.dismiss();
73    }
74
75    @Override
76    protected void onCreate(Bundle savedInstanceState) {
77        super.onCreate(savedInstanceState);
78
79        Intent i = getIntent();
80        String action = i.getAction();
81        if (!action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
82            Log.e(TAG, "Error: this activity may be started only with intent "
83                  + "ACTION_CONNECTION_ACCESS_REQUEST");
84            finish();
85            return;
86        }
87
88        mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
89        mReturnPackage = i.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME);
90        mReturnClass = i.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME);
91        mRequestType = i.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
92                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
93
94        if(DEBUG) Log.i(TAG, "onCreate() Request type: " + mRequestType);
95
96        if (mRequestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) {
97            showDialog(getString(R.string.bluetooth_connection_permission_request), mRequestType);
98        } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
99            showDialog(getString(R.string.bluetooth_phonebook_request), mRequestType);
100        } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
101            showDialog(getString(R.string.bluetooth_map_request), mRequestType);
102        }
103        else {
104            Log.e(TAG, "Error: bad request type: " + mRequestType);
105            finish();
106            return;
107        }
108        registerReceiver(mReceiver,
109                         new IntentFilter(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL));
110        mReceiverRegistered = true;
111    }
112
113
114    private void showDialog(String title, int requestType)
115    {
116        final AlertController.AlertParams p = mAlertParams;
117        p.mTitle = title;
118        if(DEBUG) Log.i(TAG, "showDialog() Request type: " + mRequestType + " this: " + this);
119        switch(requestType)
120        {
121        case BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION:
122            p.mView = createConnectionDialogView();
123            break;
124        case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS:
125            p.mView = createPhonebookDialogView();
126            break;
127        case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS:
128            p.mView = createMapDialogView();
129            break;
130        }
131        p.mPositiveButtonText = getString(R.string.yes);
132        p.mPositiveButtonListener = this;
133        p.mNegativeButtonText = getString(R.string.no);
134        p.mNegativeButtonListener = this;
135        mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
136        setupAlert();
137
138    }
139    @Override
140    public void onBackPressed() {
141        /*we need an answer so ignore back button presses during auth */
142        if(DEBUG) Log.i(TAG, "Back button pressed! ignoring");
143        return;
144    }
145    private String createRemoteName()
146    {
147        String mRemoteName = mDevice != null ? mDevice.getAliasName() : null;
148
149        if (mRemoteName == null) mRemoteName = getString(R.string.unknown);
150        return mRemoteName;
151    }
152
153    // TODO(edjee): createConnectionDialogView, createPhonebookDialogView and createMapDialogView
154    // are similar. Refactor them into one method.
155    // Also, the string resources bluetooth_remember_choice and bluetooth_pb_remember_choice should
156    // be removed.
157    private View createConnectionDialogView() {
158        String mRemoteName = createRemoteName();
159        mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
160        messageView = (TextView)mView.findViewById(R.id.message);
161        messageView.setText(getString(R.string.bluetooth_connection_dialog_text,
162                mRemoteName));
163        return mView;
164    }
165
166    private View createPhonebookDialogView() {
167        String mRemoteName = createRemoteName();
168        mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
169        messageView = (TextView)mView.findViewById(R.id.message);
170        messageView.setText(getString(R.string.bluetooth_pb_acceptance_dialog_text,
171                mRemoteName, mRemoteName));
172        return mView;
173    }
174
175    private View createMapDialogView() {
176        String mRemoteName = createRemoteName();
177        mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
178        messageView = (TextView)mView.findViewById(R.id.message);
179        messageView.setText(getString(R.string.bluetooth_map_acceptance_dialog_text,
180                mRemoteName, mRemoteName));
181        return mView;
182    }
183
184    private void onPositive() {
185        if (DEBUG) Log.d(TAG, "onPositive");
186        sendReplyIntentToReceiver(true, true);
187        finish();
188    }
189
190    private void onNegative() {
191        if (DEBUG) Log.d(TAG, "onNegative");
192
193        boolean always = true;
194        if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
195            LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(this);
196            CachedBluetoothDeviceManager cachedDeviceManager =
197                    bluetoothManager.getCachedDeviceManager();
198            CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice);
199            if (cachedDevice == null) {
200                cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(),
201                                                             bluetoothManager.getProfileManager(),
202                                                             mDevice);
203            }
204            always = cachedDevice.checkAndIncreaseMessageRejectionCount();
205        }
206
207        sendReplyIntentToReceiver(false, always);
208    }
209
210    private void sendReplyIntentToReceiver(final boolean allowed, final boolean always) {
211        Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
212
213        if (mReturnPackage != null && mReturnClass != null) {
214            intent.setClassName(mReturnPackage, mReturnClass);
215        }
216        if (DEBUG) Log.i(TAG, "sendReplyIntentToReceiver() Request type: " + mRequestType +
217                " mReturnPackage" + mReturnPackage + " mReturnClass" + mReturnClass);
218
219        intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
220                        allowed ? BluetoothDevice.CONNECTION_ACCESS_YES
221                                : BluetoothDevice.CONNECTION_ACCESS_NO);
222        intent.putExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, always);
223        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
224        intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType);
225        sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN);
226    }
227
228    public void onClick(DialogInterface dialog, int which) {
229        switch (which) {
230            case DialogInterface.BUTTON_POSITIVE:
231                onPositive();
232                break;
233
234            case DialogInterface.BUTTON_NEGATIVE:
235                onNegative();
236                break;
237            default:
238                break;
239        }
240    }
241
242    @Override
243    protected void onDestroy() {
244        super.onDestroy();
245        if (mReceiverRegistered) {
246            unregisterReceiver(mReceiver);
247            mReceiverRegistered = false;
248        }
249    }
250
251    public boolean onPreferenceChange(Preference preference, Object newValue) {
252        return true;
253    }
254}
255