/*******************************************************************************
* Copyright (c) 2011 Adam Shanks (ChainsDD)
*
* 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 com.noshufou.android.su;
import com.noshufou.android.su.preferences.Preferences;
import com.noshufou.android.su.provider.PermissionsProvider.Apps;
import com.noshufou.android.su.util.Util;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.ViewFlipper;
import java.io.IOException;
import java.io.OutputStream;
public class SuRequestActivity extends Activity implements OnClickListener {
private static final String TAG = "Su.SuRequestActivity";
private LocalSocket mSocket;
private SharedPreferences mPrefs;
private int mCallerUid = 0;
private int mDesiredUid = 0;
private String mDesiredCmd = "";
private int mSuVersionCode = 0;
private boolean mUsePin = false;
private int mAttempts = 3;
private NfcAdapter mNfcAdapter = null;
private CheckBox mRememberCheckBox;
private EditText mPinText;
private ViewFlipper mFlipper;
private TextView mMoreInfo;
@Override
public void onCreate(Bundle savedInstanceState) {
String socketPath;
super.onCreate(savedInstanceState);
if (this.getCallingPackage() != null) {
Log.e(TAG, "SuRequest must be started from su");
finish();
return;
}
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
Intent intent = this.getIntent();
mCallerUid = intent.getIntExtra(SuRequestReceiver.EXTRA_CALLERUID, 0);
mDesiredUid = intent.getIntExtra(SuRequestReceiver.EXTRA_UID, 0);
mDesiredCmd = intent.getStringExtra(SuRequestReceiver.EXTRA_CMD);
socketPath = intent.getStringExtra(SuRequestReceiver.EXTRA_SOCKET);
mSuVersionCode = intent.getIntExtra(SuRequestReceiver.EXTRA_VERSION_CODE, 0);
mUsePin = mPrefs.getBoolean(Preferences.PIN, false);
if (mUsePin) {
this.setContentView(R.layout.activity_request_pin);
ViewGroup pinLayout = (ViewGroup) findViewById(R.id.pin_layout);
mPinText = (EditText) pinLayout.findViewById(R.id.pin);
((Button)pinLayout.findViewById(R.id.pin_0)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_1)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_2)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_3)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_4)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_5)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_6)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_7)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_8)).setOnClickListener(onPinButton);
((Button)pinLayout.findViewById(R.id.pin_9)).setOnClickListener(onPinButton);
((Button)findViewById(R.id.pin_ok)).setOnClickListener(this);
((Button)findViewById(R.id.pin_cancel)).setOnClickListener(this);
} else {
this.setContentView(R.layout.activity_request);
((Button)findViewById(R.id.allow)).setOnClickListener(this);
((Button)findViewById(R.id.deny)).setOnClickListener(this);
}
try {
mSocket = new LocalSocket();
mSocket.connect(new LocalSocketAddress(socketPath,
LocalSocketAddress.Namespace.FILESYSTEM));
} catch (IOException e) {
// If we can't connect to the socket, there's no point in
// being here. Log it and quit
Log.e(TAG, "Failed to connect to socket", e);
finish();
}
if (mSuVersionCode < 10) {
Util.showOutdatedNotification(this);
}
TextView message = (TextView) findViewById(R.id.message);
message.setText(getString(R.string.request_message, Util.getAppName(this, mCallerUid, false)));
TextView appNameView = (TextView) findViewById(R.id.app_name);
appNameView.setText(Util.getAppName(this, mCallerUid, true));
TextView packageNameView = (TextView) findViewById(R.id.package_name);
packageNameView.setText(Util.getAppPackage(this, mCallerUid));
TextView requestDetailView = (TextView) findViewById(R.id.request_detail);
requestDetailView.setText(Util.getUidName(this, mDesiredUid, true));
TextView commandView = (TextView)findViewById(R.id.command);
commandView.setText(mDesiredCmd);
mRememberCheckBox = (CheckBox) findViewById(R.id.check_remember);
mRememberCheckBox.setChecked(mPrefs.getBoolean("last_remember_value", true));
mFlipper = (ViewFlipper) findViewById(R.id.flipper);
mMoreInfo = (TextView)findViewById(R.id.more_info);
if (mPrefs.getBoolean(Preferences.ADVANCED_PROMPT, false)) {
mFlipper.setDisplayedChild(1);
mMoreInfo.setVisibility(View.GONE);
} else {
mFlipper.setOnClickListener(this);
mMoreInfo.setOnClickListener(this);
}
}
@Override
protected void onResume() {
super.onResume();
if (mPrefs.getBoolean(Preferences.USE_ALLOW_TAG, false)) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("text/x-su-a");
} catch (MalformedMimeTypeException e) {
Log.e(TAG, "Bad MIME type declared", e);
return;
}
IntentFilter[] filters = new IntentFilter[] { ndef };
String[][] techLists = new String[][] {
new String[] { Ndef.class.getName() }
};
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, filters, techLists);
}
}
@Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
sendResult(false, false);
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.allow:
case R.id.pin_ok:
if (mUsePin) {
mAttempts--;
if (Util.checkPin(this, mPinText.getText().toString())) {
sendResult(true, mRememberCheckBox.isChecked());
} else if (mAttempts > 0) {
mPinText.setText("");
mPinText.setHint(getResources().getQuantityString(R.plurals.pin_incorrect_try,
mAttempts, mAttempts));
mPinText.setHintTextColor(Color.RED);
} else {
sendResult(false, false);
}
} else {
sendResult(true, mRememberCheckBox.isChecked());
}
break;
case R.id.deny:
case R.id.pin_cancel:
sendResult(false, mRememberCheckBox.isChecked());
break;
case R.id.flipper:
case R.id.more_info:
flipInfo();
}
}
private void flipInfo() {
mFlipper.showNext();
if (mFlipper.getDisplayedChild() == 0) {
mMoreInfo.setText(R.string.request_more_info);
} else {
mMoreInfo.setText(R.string.request_less_info);
}
}
private View.OnClickListener onPinButton = new View.OnClickListener() {
public void onClick(View view) {
Button button = (Button) view;
mPinText.append(button.getText());
}
};
private void sendResult(boolean allow, boolean remember) {
String resultCode = allow ? "ALLOW" : "DENY";
if (remember) {
ContentValues values = new ContentValues();
values.put(Apps.UID, mCallerUid);
values.put(Apps.PACKAGE, Util.getAppPackage(this, mCallerUid));
values.put(Apps.NAME, Util.getAppName(this, mCallerUid, false));
values.put(Apps.EXEC_UID, mDesiredUid);
values.put(Apps.EXEC_CMD, mDesiredCmd);
values.put(Apps.ALLOW, allow?Apps.AllowType.ALLOW:Apps.AllowType.DENY);
getContentResolver().insert(Apps.CONTENT_URI, values);
}
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean("last_remember_value", mRememberCheckBox.isChecked());
int timeout = mPrefs.getInt(Preferences.TIMEOUT, 0);
if (timeout > 0 && allow) {
String key = "active_" + mCallerUid;
editor.putLong(key, System.currentTimeMillis() + (timeout * 1000));
}
editor.commit();
try {
OutputStream os = mSocket.getOutputStream();
Log.i(TAG, "Sending result: " + resultCode + " for UID: " + mCallerUid);
os.write(resultCode.getBytes("UTF-8"));
os.flush();
os.close();
mSocket.close();
} catch (IOException e) {
Log.e(TAG, "Failed to write to socket", e);
}
finish();
}
@Override
public void onNewIntent(Intent intent) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
NdefMessage msg = (NdefMessage) rawMsgs[0];
NdefRecord record = msg.getRecords()[0];
short tnf = record.getTnf();
String type = new String(record.getType());
if (tnf == NdefRecord.TNF_MIME_MEDIA &&
type.equals("text/x-su-a")) {
String tagPin = new String(record.getPayload());
if (tagPin.equals(mPrefs.getString("pin", ""))) {
sendResult(true, mRememberCheckBox.isChecked());
}
}
}
}
}