commit 42a5885e5732ef869d246faa1d11a38de2e07ea4 Author: rjh <1409667648@qq.com> Date: Sat Sep 19 23:13:07 2020 +0800 前期文件与源码 diff --git a/doc/XPrivacy Analysis.pdf b/doc/XPrivacy Analysis.pdf new file mode 100644 index 0000000..dfbe04a Binary files /dev/null and b/doc/XPrivacy Analysis.pdf differ diff --git a/resources/XposedBridgeApi-82.jar b/resources/XposedBridgeApi-82.jar new file mode 100644 index 0000000..eec212f Binary files /dev/null and b/resources/XposedBridgeApi-82.jar differ diff --git a/src/FakeLocation-master/FakeLocation-master/.gitignore b/src/FakeLocation-master/FakeLocation-master/.gitignore new file mode 100644 index 0000000..09b993d --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/src/FakeLocation-master/FakeLocation-master/.project b/src/FakeLocation-master/FakeLocation-master/.project new file mode 100644 index 0000000..908a7af --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/.project @@ -0,0 +1,17 @@ + + + FakeLocation-master + Project FakeLocation-master created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/src/FakeLocation-master/FakeLocation-master/.settings/org.eclipse.buildship.core.prefs b/src/FakeLocation-master/FakeLocation-master/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..6b905d3 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=C\:/Program Files/Java/jdk-13.0.1 +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/src/FakeLocation-master/FakeLocation-master/app/.gitignore b/src/FakeLocation-master/FakeLocation-master/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/src/FakeLocation-master/FakeLocation-master/app/build.gradle b/src/FakeLocation-master/FakeLocation-master/app/build.gradle new file mode 100644 index 0000000..c1c61f0 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/build.gradle @@ -0,0 +1,40 @@ +apply plugin: 'com.android.application' + +def releaseAppName() { + return "FakeLocation" +} + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.xposed.hook" + minSdkVersion 15 + targetSdkVersion 23 + versionCode 3 + versionName "1.0.3" + } + buildTypes { + release { + minifyEnabled false + applicationVariants.all { + variant -> + variant.outputs.each { + output -> output.outputFileName = "${releaseAppName()}_v${defaultConfig.versionName}.apk" + } + } + } + } + lintOptions { + checkReleaseBuilds false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:design:28.0.0' + compileOnly 'de.robv.android.xposed:api:82' +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/proguard-rules.pro b/src/FakeLocation-master/FakeLocation-master/app/proguard-rules.pro new file mode 100644 index 0000000..9786e21 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\Android\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/AndroidManifest.xml b/src/FakeLocation-master/FakeLocation-master/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d18a28c --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/assets/xposed_init b/src/FakeLocation-master/FakeLocation-master/app/src/main/assets/xposed_init new file mode 100644 index 0000000..f358a50 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/assets/xposed_init @@ -0,0 +1 @@ +com.xposed.hook.Main \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/LuckMoneySetting.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/LuckMoneySetting.java new file mode 100644 index 0000000..bf48b9b --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/LuckMoneySetting.java @@ -0,0 +1,101 @@ +package com.xposed.hook; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Settings; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.EditText; + +import com.xposed.hook.wechat.LuckyMoneyHook; + +/** + * Created by lin on 2018/2/4. + */ + +public class LuckMoneySetting extends AppCompatActivity { + + private CompoundButton cb; + private CompoundButton cb2; + private CompoundButton cb3; + private CompoundButton cb4; + private EditText et_lucky_money_delay; + + private SharedPreferences sp; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_lucky_money_setting); + setTitle(R.string.wechat_hook); + cb = (CompoundButton) findViewById(R.id.cb); + cb2 = (CompoundButton) findViewById(R.id.cb2); + cb3 = (CompoundButton) findViewById(R.id.cb3); + cb4 = (CompoundButton) findViewById(R.id.cb4); + et_lucky_money_delay = findViewById(R.id.et_lucky_money_delay); + sp = getSharedPreferences("lucky_money", MODE_WORLD_READABLE); + cb.setChecked(sp.getBoolean("quick_open", true)); + cb2.setChecked(sp.getBoolean("auto_receive", true)); + cb3.setChecked(sp.getBoolean("recalled", true)); + cb4.setChecked(sp.getBoolean("3_days_Moments", false)); + et_lucky_money_delay.setText(String.valueOf(sp.getInt("lucky_money_delay", 0))); + cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + sp.edit().putBoolean("quick_open", isChecked).commit(); + } + }); + cb2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + sp.edit().putBoolean("auto_receive", isChecked).commit(); + } + }); + cb3.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + sp.edit().putBoolean("recalled", isChecked).commit(); + } + }); + cb4.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + sp.edit().putBoolean("3_days_Moments", isChecked).commit(); + } + }); + findViewById(R.id.btn_reboot_app).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + saveLuckyMoneyDelay(); + try { + Intent intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", LuckyMoneyHook.WECHAT_PACKAGE_NAME, null)); + startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public void finish() { + super.finish(); + saveLuckyMoneyDelay(); + } + + private void saveLuckyMoneyDelay() { + try { + int delay = Integer.parseInt(et_lucky_money_delay.getText().toString()); + sp.edit().putInt("lucky_money_delay", delay).commit(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/Main.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/Main.java new file mode 100644 index 0000000..1d02ffa --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/Main.java @@ -0,0 +1,46 @@ +package com.xposed.hook; + +import android.util.Log; + +import com.xposed.hook.config.Constants; +import com.xposed.hook.config.PkgConfig; +import com.xposed.hook.location.LocationHook; +import com.xposed.hook.wechat.LuckyMoneyHook; + +import de.robv.android.xposed.IXposedHookLoadPackage; +import de.robv.android.xposed.XSharedPreferences; +import de.robv.android.xposed.callbacks.XC_LoadPackage; + +/** + * Created by lin on 2017/7/22. + */ + +public class Main implements IXposedHookLoadPackage { + + @Override + public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { + Log.e("***********************", loadPackageParam.packageName); + LuckyMoneyHook.hook(loadPackageParam); + XSharedPreferences preferences = new XSharedPreferences("com.xposed.hook", Constants.PREF_FILE_NAME); + if (preferences.getBoolean(loadPackageParam.packageName, false)) { + String defaultLatitude = Constants.DEFAULT_LATITUDE; + String defaultLongitude = Constants.DEFAULT_LONGITUDE; + if (PkgConfig.pkg_dingtalk.equals(loadPackageParam.packageName)) { + defaultLatitude = "0"; + defaultLongitude = "0"; + } + String prefix = loadPackageParam.packageName + "_"; + double latitude = 0; + double longitude = 0; + try { + latitude = Double.parseDouble(preferences.getString(prefix + "latitude", defaultLatitude)); + longitude = Double.parseDouble(preferences.getString(prefix + "longitude", defaultLongitude)); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + int lac = preferences.getInt(prefix + "lac", Constants.DEFAULT_LAC); + int cid = preferences.getInt(prefix + "cid", Constants.DEFAULT_CID); + LocationHook.hookAndChange(loadPackageParam, latitude, longitude, lac, cid); + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/MainActivity.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/MainActivity.java new file mode 100644 index 0000000..cfeb432 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/MainActivity.java @@ -0,0 +1,96 @@ +package com.xposed.hook; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import com.xposed.hook.entity.AppInfo; +import com.xposed.hook.utils.AppUtil; +import com.xposed.hook.utils.ViewHolder; + +import java.io.Serializable; +import java.util.List; + +public class MainActivity extends AppCompatActivity { + + private ListView lv; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + lv = (ListView) findViewById(R.id.lv); + lv.setAdapter(new MyAdapter(getApplicationContext())); + lv.setOnItemClickListener((parent, view, position, id) -> { + startActivity(new Intent(this, RimetActivity.class) + .putExtra("appInfo", (Serializable) lv.getAdapter().getItem(position))); + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_main, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == R.id.item_luck_money) { + startActivity(new Intent(this, LuckMoneySetting.class)); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + private static class MyAdapter extends BaseAdapter { + + private List list; + + public MyAdapter(Context context) { + list = AppUtil.getAppList(context); + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public AppInfo getItem(int position) { + return list.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) + convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_package, parent, false); + ImageView iv_icon = ViewHolder.get(convertView, R.id.iv_icon); + TextView tv_title = ViewHolder.get(convertView, R.id.tv_title); + TextView tv_package = ViewHolder.get(convertView, R.id.tv_package); + AppInfo pkg = list.get(position); + iv_icon.setImageDrawable(pkg.icon); + tv_title.setText(pkg.title); + tv_package.setText(pkg.packageName); + return convertView; + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/RimetActivity.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/RimetActivity.java new file mode 100644 index 0000000..18d033b --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/RimetActivity.java @@ -0,0 +1,269 @@ +package com.xposed.hook; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.design.widget.TabLayout; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.telephony.CellLocation; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.telephony.gsm.GsmCellLocation; +import android.view.View; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.xposed.hook.config.Constants; +import com.xposed.hook.config.PkgConfig; +import com.xposed.hook.entity.AppInfo; + +public class RimetActivity extends AppCompatActivity implements View.OnClickListener { + + private SharedPreferences sp; + + private TabLayout tabLayout; + private TabLayout.Tab gpsTab; + private TabLayout.Tab cellTab; + + private View ll_gps; + private EditText etLatitude; + private EditText etLongitude; + private TextView tvLatitude; + private TextView tvLongitude; + private Button btnAutoFillGps; + + private View ll_cell; + private EditText etLac; + private EditText etCid; + private TextView tvLac; + private TextView tvCid; + private Button btnAutoFillCell; + + private CompoundButton cb; + + private AppInfo appInfo; + private boolean isDingTalk; + + TelephonyManager tm; + GsmCellLocation l; + LocationManager lm; + Location gpsL; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + appInfo = (AppInfo) getIntent().getSerializableExtra("appInfo"); + if (appInfo == null) + return; + setContentView(R.layout.activity_rimet); + setTitle(appInfo.title); + isDingTalk = PkgConfig.pkg_dingtalk.equals(appInfo.packageName); + tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE); + + String prefix = appInfo.packageName + "_"; + sp = getSharedPreferences(Constants.PREF_FILE_NAME, MODE_WORLD_READABLE); + String defaultLatitude = Constants.DEFAULT_LATITUDE; + String defaultLongitude = Constants.DEFAULT_LONGITUDE; + if (isDingTalk) { + defaultLatitude = ""; + defaultLongitude = ""; + } + + ll_gps = findViewById(R.id.ll_gps); + etLatitude = (EditText) findViewById(R.id.et_latitude); + etLongitude = (EditText) findViewById(R.id.et_longitude); + tvLatitude = (TextView) findViewById(R.id.tv_latitude); + tvLongitude = (TextView) findViewById(R.id.tv_longitude); + btnAutoFillGps = (Button) findViewById(R.id.btn_auto_fill_gps); + etLatitude.setText(sp.getString(prefix + "latitude", defaultLatitude)); + etLongitude.setText(sp.getString(prefix + "longitude", defaultLongitude)); + btnAutoFillGps.setOnClickListener(this); + + ll_cell = findViewById(R.id.ll_cell); + etLac = (EditText) findViewById(R.id.et_lac); + etCid = (EditText) findViewById(R.id.et_cid); + tvLac = (TextView) findViewById(R.id.tv_lac); + tvCid = (TextView) findViewById(R.id.tv_cid); + btnAutoFillCell = (Button) findViewById(R.id.btn_auto_fill_cell); + int lac = sp.getInt(prefix + "lac", Constants.DEFAULT_LAC); + int cid = sp.getInt(prefix + "cid", Constants.DEFAULT_CID); + if (lac != Constants.DEFAULT_LAC) + etLac.setText(String.valueOf(lac)); + if (cid != Constants.DEFAULT_CID) + etCid.setText(String.valueOf(cid)); + btnAutoFillCell.setOnClickListener(this); + + initTabLayout(); + cb = (CompoundButton) findViewById(R.id.cb); + cb.setChecked(sp.getBoolean(appInfo.packageName, false)); + findViewById(R.id.btn_save).setOnClickListener(this); + findViewById(R.id.btn_reboot_app).setOnClickListener(this); + + requestPermissions(); + } + + private void initTabLayout() { + tabLayout = (TabLayout) findViewById(R.id.tab_layout); + gpsTab = tabLayout.newTab().setText(R.string.gps_location); + cellTab = tabLayout.newTab().setText(R.string.cell_location); + if (isDingTalk) { + tabLayout.addTab(cellTab); + tabLayout.addTab(gpsTab); + ll_gps.setVisibility(View.GONE); + } else { + tabLayout.addTab(gpsTab); + tabLayout.addTab(cellTab); + ll_cell.setVisibility(View.GONE); + } + tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + ll_gps.setVisibility(tab == gpsTab ? View.VISIBLE : View.GONE); + ll_cell.setVisibility(tab == gpsTab ? View.GONE : View.VISIBLE); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + + } + }); + } + + @Override + public void onClick(View v) { + int id = v.getId(); + switch (id) { + case R.id.btn_auto_fill_cell: + etLac.setText(String.valueOf(l.getLac())); + etCid.setText(String.valueOf(l.getCid())); + break; + case R.id.btn_auto_fill_gps: + etLatitude.setText(String.valueOf(gpsL.getLatitude())); + etLongitude.setText(String.valueOf(gpsL.getLongitude())); + break; + case R.id.btn_save: + String prefix = appInfo.packageName + "_"; + sp.edit().putString(prefix + "latitude", etLatitude.getText().toString()) + .putString(prefix + "longitude", etLongitude.getText().toString()) + .putInt(prefix + "lac", parseInt(etLac.getText().toString())) + .putInt(prefix + "cid", parseInt(etCid.getText().toString())) + .putBoolean(appInfo.packageName, cb.isChecked()) + .commit(); + Toast.makeText(getApplicationContext(), R.string.save_success, Toast.LENGTH_SHORT).show(); + break; + case R.id.btn_reboot_app: + try { + Intent intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", appInfo.packageName, null)); + startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + break; + } + } + + private int parseInt(String str) { + try { + return Integer.parseInt(str); + } catch (Exception e) { + return -1; + } + } + + @Override + public void finish() { + stopLocation(); + super.finish(); + } + + PhoneStateListener listener = new PhoneStateListener() { + + @Override + public void onCellLocationChanged(CellLocation location) { + if (location instanceof GsmCellLocation) { + l = (GsmCellLocation) location; + tvLac.setText(getString(R.string.current_lac, String.valueOf(l.getLac()))); + tvCid.setText(getString(R.string.current_cid, String.valueOf(l.getCid()))); + btnAutoFillCell.setVisibility(View.VISIBLE); + } + } + }; + + LocationListener gpsListener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + gpsL = location; + tvLatitude.setText(getString(R.string.current_latitude, String.valueOf(location.getLatitude()))); + tvLongitude.setText(getString(R.string.current_longitude, String.valueOf(location.getLongitude()))); + btnAutoFillGps.setVisibility(View.VISIBLE); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + + } + + @Override + public void onProviderEnabled(String provider) { + + } + + @Override + public void onProviderDisabled(String provider) { + + } + }; + + private void requestPermissions() { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION}, 101); + return; + } + startLocation(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == 101 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startLocation(); + } + } + + private void startLocation() { + tm.listen(listener, PhoneStateListener.LISTEN_CELL_LOCATION); + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED + && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + return; + } + lm.requestSingleUpdate(LocationManager.GPS_PROVIDER, gpsListener, null); + } + + private void stopLocation() { + tm.listen(listener, PhoneStateListener.LISTEN_NONE); + lm.removeUpdates(gpsListener); + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/config/Constants.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/config/Constants.java new file mode 100644 index 0000000..6aaacd0 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/config/Constants.java @@ -0,0 +1,11 @@ +package com.xposed.hook.config; + +public class Constants { + + public static final String PREF_FILE_NAME = "location"; + + public static final String DEFAULT_LATITUDE = "34.752600"; + public static final String DEFAULT_LONGITUDE = "113.662000"; + public static final int DEFAULT_LAC = -1; + public static final int DEFAULT_CID = -1; +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/config/PkgConfig.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/config/PkgConfig.java new file mode 100644 index 0000000..3ab5c8e --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/config/PkgConfig.java @@ -0,0 +1,23 @@ +package com.xposed.hook.config; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by lin on 2018/1/24. + */ + +public class PkgConfig { + + public static final List packages = new ArrayList<>(); + public static final String pkg_dingtalk = "com.alibaba.android.rimet"; + + static { + packages.add("com.autonavi.minimap"); + packages.add("com.team.club"); + packages.add("com.baidu.BaiduMap"); + packages.add("com.tencent.mm"); + packages.add("com.tencent.mobileqq"); + packages.add("com.sina.weibo"); + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/entity/AppInfo.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/entity/AppInfo.java new file mode 100644 index 0000000..c1e4f0b --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/entity/AppInfo.java @@ -0,0 +1,12 @@ +package com.xposed.hook.entity; + +import android.graphics.drawable.Drawable; + +import java.io.Serializable; + +public class AppInfo implements Serializable { + + public String title; + public String packageName; + public transient Drawable icon; +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/GPSStateline.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/GPSStateline.java new file mode 100644 index 0000000..8f44b44 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/GPSStateline.java @@ -0,0 +1,79 @@ +package com.xposed.hook.location; + + +class GPSStateline { + private double mAzimuth; + private double mElevation; + private boolean mHasAlmanac; + private boolean mHasEphemeris; + private int mPnr; + private double mSnr; + private boolean mUseInFix; + + + public double getAzimuth() { + return this.mAzimuth; + } + + public double getElevation() { + return this.mElevation; + } + + public int getPnr() { + return this.mPnr; + } + + public double getSnr() { + return this.mSnr; + } + + public boolean isHasAlmanac() { + return this.mHasAlmanac; + } + + public boolean isHasEphemeris() { + return this.mHasEphemeris; + } + + public boolean isUseInFix() { + return this.mUseInFix; + } + + public void setAzimuth(double azimuth) { + this.mAzimuth = azimuth; + } + + public void setElevation(double elevation) { + this.mElevation = elevation; + } + + public void setHasAlmanac(boolean hasAlmanac) { + this.mHasAlmanac = hasAlmanac; + } + + public void setHasEphemeris(boolean hasEphemeris) { + this.mHasEphemeris = hasEphemeris; + } + + public void setPnr(int pnr) { + this.mPnr = pnr; + } + + public void setSnr(double snr) { + this.mSnr = snr; + } + + public void setUseInFix(boolean useInFix) { + this.mUseInFix = useInFix; + } + + public GPSStateline(int pnr, double snr, double elevation, double azimuth, boolean useInFix, boolean hasAlmanac, boolean hasEphemeris) { + this.mPnr = pnr; + this.mSnr = snr; + this.mElevation = elevation; + this.mAzimuth = azimuth; + this.mUseInFix = useInFix; + this.mHasAlmanac = hasAlmanac; + this.mHasEphemeris = hasEphemeris; + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationHandler.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationHandler.java new file mode 100644 index 0000000..c3ed656 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationHandler.java @@ -0,0 +1,154 @@ +package com.xposed.hook.location; + +import android.content.Context; +import android.location.Location; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.util.Log; + +import java.util.Map; +import java.util.Set; + +/** + * Created by lin on 2017/8/6. + */ + +public class LocationHandler extends Handler { + + private static LocationHandler instance; + public static double latitude, longitude; + + public static LocationHandler getInstance() { + if (instance == null) { + synchronized (LocationHandler.class) { + if (instance == null) + instance = new LocationHandler(); + } + } + return instance; + } + + private Context context; + + private LocationHandler() { + super(Looper.getMainLooper()); + } + + public void attach(Context context) { + this.context = context; + } + + @Override + public void handleMessage(Message msg) { + try { + Object transport = context.getSystemService(Context.LOCATION_SERVICE); + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + notifyGPSStatus(LocationManager.mGnssStatusListeners.get(transport)); + notifyMNmeaListener(LocationManager.mGnssNmeaListeners.get(transport)); + notifyGPSStatus(LocationManager.mGpsStatusListeners.get(transport)); + notifyMNmeaListener(LocationManager.mGpsNmeaListeners.get(transport)); + } else { + notifyGPSStatus(LocationManager.mGpsStatusListeners.get(transport)); + notifyMNmeaListener(LocationManager.mNmeaListeners.get(transport)); + } + } catch (Throwable e) { + e.printStackTrace(); + } + notifyLocation(LocationManager.mListeners.get(transport)); + sendEmptyMessageDelayed(0, 10000); + Log.e(LocationHook.TAG, "Avalon Hook Location Success"); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public static Location createLocation(double latitude, double longitude) { + Location l = new Location(android.location.LocationManager.GPS_PROVIDER); + l.setLatitude(latitude); + l.setLongitude(longitude); + l.setAccuracy(8f); + l.setTime(System.currentTimeMillis()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + l.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); + } + Bundle extraBundle = new Bundle(); + l.setExtras(extraBundle); + int svCount = VirtualGPSSatalines.get().getSvCount(); + extraBundle.putInt("satellites", svCount); + extraBundle.putInt("satellitesvalue", svCount); + return l; + } + + public static void updateLocation(Location location, double latitude, double longitude) { + location.setLatitude(latitude); + location.setLongitude(longitude); + location.setTime(System.currentTimeMillis()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); + } + } + + public void start() { + removeMessages(0); + sendEmptyMessageDelayed(0, 1000); + } + + private void notifyGPSStatus(Map listeners) { + if (listeners != null && !listeners.isEmpty()) { + //noinspection unchecked + Set entries = listeners.entrySet(); + for (Map.Entry entry : entries) { + try { + Object value = entry.getValue(); + if (value != null) { + MockLocationHelper.invokeSvStatusChanged(value); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + + private void notifyLocation(Map listeners) { + if (listeners != null && !listeners.isEmpty()) { + Location location = createLocation(latitude, longitude); + //noinspection unchecked + Set entries = listeners.entrySet(); + for (Map.Entry entry : entries) { + Object value = entry.getValue(); + if (value != null) { + try { + Log.e(LocationHook.TAG, value.toString()); + LocationManager.ListenerTransport.onLocationChanged.call(value, location); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + } + + private void notifyMNmeaListener(Map listeners) { + if (listeners != null && !listeners.isEmpty()) { + //noinspection unchecked + Set entries = listeners.entrySet(); + for (Map.Entry entry : entries) { + try { + Object value = entry.getValue(); + if (value != null) { + MockLocationHelper.invokeNmeaReceived(value); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationHook.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationHook.java new file mode 100644 index 0000000..ee87f39 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationHook.java @@ -0,0 +1,234 @@ +package com.xposed.hook.location; + +import android.content.Context; +import android.location.Location; +import android.location.LocationListener; +import android.os.Build; +import android.telephony.PhoneStateListener; +import android.util.Log; + +import com.xposed.hook.location.LocationHandler; +import com.xposed.hook.location.PhoneStateListenerDelegate; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; +import de.robv.android.xposed.callbacks.XC_LoadPackage; + +/** + * Created by lin on 2017/7/23. + */ + +public class LocationHook { + + public static String TAG = "LocationHook"; + + public static void hookAndChange(XC_LoadPackage.LoadPackageParam mLpp, final double latitude, final double longitude, final int lac, final int cid) { + + Log.e(TAG, "Avalon Hook Location Test: " + mLpp.packageName); + LocationHandler.latitude = latitude; + LocationHandler.longitude = longitude; + + hookMethod("android.content.ContextWrapper", mLpp.classLoader, "getApplicationContext", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + LocationHandler.getInstance().attach((Context) param.getResult()); + } + }); + + hookMethod("android.net.wifi.WifiManager", mLpp.classLoader, "getScanResults", + new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) + throws Throwable { + param.setResult(null); + } + }); + + hookMethod("android.telephony.TelephonyManager", mLpp.classLoader, "getCellLocation", + new XC_MethodHook() { + /** + * android.telephony.TelephonyManager的getCellLocation方法 + * Returns the current location of the device. + * Return null if current location is not available. + */ + @Override + protected void afterHookedMethod(MethodHookParam param) + throws Throwable { + param.setResult(null); + } + }); + + hookMethod("android.telephony.TelephonyManager", mLpp.classLoader, "getNeighboringCellInfo", + new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) + throws Throwable { + param.setResult(null); + } + }); + + hookMethods("android.location.LocationManager", "requestLocationUpdates", + new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + + if (param.args[3] instanceof LocationListener) { + //位置监听器,当位置改变时会触发onLocationChanged方法 + LocationListener ll = (LocationListener) param.args[3]; + Log.e(TAG, "requestLocationUpdates::: args0: " + param.args[0] + "; arg1: " + param.args[1] + "; arg2: " + param.args[2]); + LocationHandler.getInstance().start(); + } + } + }); + + hookMethod("android.location.LocationManager", mLpp.classLoader, "getLastLocation", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Location l = (Location) param.getResult(); + if (l != null) { + LocationHandler.updateLocation(l, latitude, longitude); + param.setResult(l); + } else + param.setResult(LocationHandler.createLocation(latitude, longitude)); + } + }); + + hookMethods("android.location.LocationManager", "getLastKnownLocation", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Location l = (Location) param.getResult(); + if (l != null) { + LocationHandler.updateLocation(l, latitude, longitude); + param.setResult(l); + } else + param.setResult(LocationHandler.createLocation(latitude, longitude)); + } + }); + + hookMethod("android.location.Location", mLpp.classLoader, "getLatitude", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + param.setResult(latitude); + } + }); + + hookMethod("android.location.Location", mLpp.classLoader, "getLongitude", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + param.setResult(longitude); + } + }); + + hookMethod("android.net.wifi.WifiInfo", mLpp.classLoader, "getMacAddress", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + param.setResult("00-00-00-00-00-00-00-E0"); + } + }); + + hookMethod("android.net.wifi.WifiInfo", mLpp.classLoader, "getSSID", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + param.setResult(null); + } + }); + + hookMethod("android.net.wifi.WifiInfo", mLpp.classLoader, "getBSSID", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + param.setResult("00:00:00:00:00:00"); + } + }); + + hookMethods("android.telephony.TelephonyManager", "getSimState", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Log.e(TAG, "getSimState"); + param.setResult(0); + } + }); + + hookMethods("android.location.LocationManager", "getBestProvider", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Log.e(TAG, "getBestProvider"); + param.setResult("gps"); + } + }); + + hookMethods("android.location.LocationManager", "getProviders", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Log.e(TAG, "getProviders"); + } + }); + + hookMethods("android.location.LocationManager", "isProviderEnabled", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Log.e(TAG, "isProviderEnabled: " + param.args[0]); + if ("gps".equals(param.args[0])) + param.setResult(true); + } + }); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { + hookMethod("android.telephony.TelephonyManager", mLpp.classLoader, + "getAllCellInfo", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Log.e(TAG, "getAllCellInfo"); + param.setResult(null); + } + }); + } + + hookMethods("android.telephony.TelephonyManager", "listen", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + Log.e(TAG, "TelephonyManager listen"); + param.args[0] = new PhoneStateListenerDelegate((PhoneStateListener) param.args[0], lac, cid); + } + }); + } + + //不带参数的方法拦截 + private static void hookMethod(Class clazz, String methodName, Object... parameterTypesAndCallback) { + try { + XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback); + } catch (Throwable e) { + Log.e(TAG, e.toString()); + } + } + + //不带参数的方法拦截 + private static void hookMethod(String className, ClassLoader classLoader, String methodName, + Object... parameterTypesAndCallback) { + try { + XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback); + } catch (Throwable e) { + Log.e(TAG, e.toString()); + } + } + + //带参数的方法拦截 + private static void hookMethods(String className, String methodName, XC_MethodHook xmh) { + try { + Class clazz = Class.forName(className); + + for (Method method : clazz.getDeclaredMethods()) + if (method.getName().equals(methodName) + && !Modifier.isAbstract(method.getModifiers()) + && Modifier.isPublic(method.getModifiers())) { + XposedBridge.hookMethod(method, xmh); + } + } catch (Throwable e) { + Log.e(TAG, e.toString()); + } + } + +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationManager.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationManager.java new file mode 100644 index 0000000..586d51e --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/LocationManager.java @@ -0,0 +1,82 @@ +package com.xposed.hook.location; + +import android.location.Location; +import android.location.LocationListener; +import android.os.Bundle; + +import java.util.HashMap; + +import mirror.MethodParams; +import mirror.RefClass; +import mirror.RefMethod; +import mirror.RefObject; + +public class LocationManager { + public static Class TYPE = RefClass.load(LocationManager.class, "android.location.LocationManager"); + public static RefObject mGnssNmeaListeners; + public static RefObject mGnssStatusListeners; + public static RefObject mGpsNmeaListeners; + public static RefObject mGpsStatusListeners; + public static RefObject mListeners; + public static RefObject mNmeaListeners; + + public static class GnssStatusListenerTransport { + public static Class TYPE = RefClass.load(GnssStatusListenerTransport.class, "android.location.LocationManager$GnssStatusListenerTransport"); + public static RefObject mGpsListener; + public static RefObject mGpsNmeaListener; + @MethodParams({int.class}) + public static RefMethod onFirstFix; + public static RefMethod onGnssStarted; + @MethodParams({long.class, String.class}) + public static RefMethod onNmeaReceived; + @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class}) + public static RefMethod onSvStatusChanged; + public static RefObject this$0; + } + + public static class GpsStatusListenerTransport { + public static Class TYPE = RefClass.load(GpsStatusListenerTransport.class, "android.location.LocationManager$GpsStatusListenerTransport"); + public static RefObject mListener; + public static RefObject mNmeaListener; + @MethodParams({int.class}) + public static RefMethod onFirstFix; + public static RefMethod onGpsStarted; + @MethodParams({long.class, String.class}) + public static RefMethod onNmeaReceived; + @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int.class, int.class, int.class}) + public static RefMethod onSvStatusChanged; + public static RefObject this$0; + } + + public static class GpsStatusListenerTransportOPPO_R815T { + public static Class TYPE = RefClass.load(GpsStatusListenerTransportOPPO_R815T.class, "android.location.LocationManager$GpsStatusListenerTransport"); + @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int[].class, int[].class, int[].class, int.class}) + public static RefMethod onSvStatusChanged; + } + + public static class GpsStatusListenerTransportSumsungS5 { + public static Class TYPE = RefClass.load(GpsStatusListenerTransportSumsungS5.class, "android.location.LocationManager$GpsStatusListenerTransport"); + @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int.class, int.class, int.class, int[].class}) + public static RefMethod onSvStatusChanged; + } + + public static class GpsStatusListenerTransportVIVO { + public static Class TYPE = RefClass.load(GpsStatusListenerTransportVIVO.class, "android.location.LocationManager$GpsStatusListenerTransport"); + @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int.class, int.class, int.class, long[].class}) + public static RefMethod onSvStatusChanged; + } + + public static class ListenerTransport { + public static Class TYPE = RefClass.load(ListenerTransport.class, "android.location.LocationManager$ListenerTransport"); + public static RefObject mListener; + @MethodParams({Location.class}) + public static RefMethod onLocationChanged; + @MethodParams({String.class}) + public static RefMethod onProviderDisabled; + @MethodParams({String.class}) + public static RefMethod onProviderEnabled; + @MethodParams({String.class, int.class, Bundle.class}) + public static RefMethod onStatusChanged; + public static RefObject this$0; + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/MockLocationHelper.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/MockLocationHelper.java new file mode 100644 index 0000000..f276606 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/MockLocationHelper.java @@ -0,0 +1,152 @@ +package com.xposed.hook.location; + +import android.location.Location; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * @author Lody + */ +public class MockLocationHelper { + + public static void invokeNmeaReceived(Object listener) { + if (listener != null) { + VirtualGPSSatalines satalines = VirtualGPSSatalines.get(); + try { + Location location = LocationHandler.createLocation(LocationHandler.latitude, LocationHandler.longitude); + if (location != null) { + String date = new SimpleDateFormat("HHmmss:SS", Locale.US).format(new Date()); + String lat = getGPSLat(LocationHandler.latitude); + String lon = getGPSLat(LocationHandler.longitude); + String latNW = getNorthWest(LocationHandler.latitude); + String lonSE = getSouthEast(LocationHandler.longitude); + String $GPGGA = checksum(String.format("$GPGGA,%s,%s,%s,%s,%s,1,%s,692,.00,M,.00,M,,,", date, lat, latNW, lon, lonSE, satalines.getSvCount())); + String $GPRMC = checksum(String.format("$GPRMC,%s,A,%s,%s,%s,%s,0,0,260717,,,A,", date, lat, latNW, lon, lonSE)); + if (LocationManager.GnssStatusListenerTransport.onNmeaReceived != null) { + LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), "$GPGSV,1,1,04,12,05,159,36,15,41,087,15,19,38,262,30,31,56,146,19,*73"); + LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPGGA); + LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), "$GPVTG,0,T,0,M,0,N,0,K,A,*25"); + LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPRMC); + LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), "$GPGSA,A,2,12,15,19,31,,,,,,,,,604,712,986,*27"); + } else if (LocationManager.GpsStatusListenerTransport.onNmeaReceived != null) { + LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), "$GPGSV,1,1,04,12,05,159,36,15,41,087,15,19,38,262,30,31,56,146,19,*73"); + LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPGGA); + LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), "$GPVTG,0,T,0,M,0,N,0,K,A,*25"); + LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPRMC); + LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), "$GPGSA,A,2,12,15,19,31,,,,,,,,,604,712,986,*27"); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + public static void invokeSvStatusChanged(Object transport) { + if (transport != null) { + VirtualGPSSatalines satalines = VirtualGPSSatalines.get(); + try { + Class aClass = transport.getClass(); + int svCount; + float[] snrs; + float[] elevations; + float[] azimuths; + if (aClass == LocationManager.GnssStatusListenerTransport.TYPE) { + svCount = satalines.getSvCount(); + int[] prnWithFlags = satalines.getPrnWithFlags(); + snrs = satalines.getSnrs(); + elevations = satalines.getElevations(); + azimuths = satalines.getAzimuths(); + LocationManager.GnssStatusListenerTransport.onSvStatusChanged.call(transport, svCount, prnWithFlags, snrs, elevations, azimuths); + } else if (aClass == LocationManager.GpsStatusListenerTransport.TYPE) { + svCount = satalines.getSvCount(); + int[] prns = satalines.getPrns(); + snrs = satalines.getSnrs(); + elevations = satalines.getElevations(); + azimuths = satalines.getAzimuths(); + int ephemerisMask = satalines.getEphemerisMask(); + int almanacMask = satalines.getAlmanacMask(); + int usedInFixMask = satalines.getUsedInFixMask(); + if (LocationManager.GpsStatusListenerTransport.onSvStatusChanged != null) { + LocationManager.GpsStatusListenerTransport.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask); + } else if (LocationManager.GpsStatusListenerTransportVIVO.onSvStatusChanged != null) { + LocationManager.GpsStatusListenerTransportVIVO.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask, new long[svCount]); + } else if (LocationManager.GpsStatusListenerTransportSumsungS5.onSvStatusChanged != null) { + LocationManager.GpsStatusListenerTransportSumsungS5.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask, new int[svCount]); + } else if (LocationManager.GpsStatusListenerTransportOPPO_R815T.onSvStatusChanged != null) { + int len = prns.length; + int[] ephemerisMasks = new int[len]; + for (int i = 0; i < len; i++) { + ephemerisMasks[i] = satalines.getEphemerisMask(); + } + int[] almanacMasks = new int[len]; + for (int i = 0; i < len; i++) { + almanacMasks[i] = satalines.getAlmanacMask(); + } + int[] usedInFixMasks = new int[len]; + for (int i = 0; i < len; i++) { + usedInFixMasks[i] = satalines.getUsedInFixMask(); + } + LocationManager.GpsStatusListenerTransportOPPO_R815T.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMasks, almanacMasks, usedInFixMasks, svCount); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private static String getSouthEast(double longitude) { + if (longitude > 0.0d) { + return "E"; + } + return "W"; + } + + private static String getNorthWest(double latitude) { + if (latitude > 0.0d) { + return "N"; + } + return "S"; + } + + public static String getGPSLat(double v) { + int du = (int) v; + double fen = (v - (double) du) * 60.0d; + return du + leftZeroPad((int) fen, 2) + ":" + String.valueOf(fen).substring(2); + } + + private static String leftZeroPad(int num, int size) { + return leftZeroPad(String.valueOf(num), size); + } + + private static String leftZeroPad(String num, int size) { + StringBuilder sb = new StringBuilder(size); + int i; + if (num == null) { + for (i = 0; i < size; i++) { + sb.append('0'); + } + } else { + for (i = 0; i < size - num.length(); i++) { + sb.append('0'); + } + sb.append(num); + } + return sb.toString(); + } + + public static String checksum(String nema) { + String checkStr = nema; + if (nema.startsWith("$")) { + checkStr = nema.substring(1); + } + int sum = 0; + for (int i = 0; i < checkStr.length(); i++) { + sum ^= (byte) checkStr.charAt(i); + } + return nema + "*" + String.format("%02X", sum).toLowerCase(); + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/PhoneStateListenerDelegate.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/PhoneStateListenerDelegate.java new file mode 100644 index 0000000..b599a19 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/PhoneStateListenerDelegate.java @@ -0,0 +1,30 @@ +package com.xposed.hook.location; + +import android.telephony.CellLocation; +import android.telephony.PhoneStateListener; +import android.telephony.gsm.GsmCellLocation; + +/** + * Created by lin on 2018/1/25. + */ + +public class PhoneStateListenerDelegate extends PhoneStateListener { + + private PhoneStateListener delegate; + private int lac; + private int cid; + + public PhoneStateListenerDelegate(PhoneStateListener delegate, int lac, int cid) { + this.delegate = delegate; + this.lac = lac; + this.cid = cid; + } + + @Override + public void onCellLocationChanged(CellLocation location) { + if(location instanceof GsmCellLocation) { + ((GsmCellLocation) location).setLacAndCid(lac, cid); + delegate.onCellLocationChanged(location); + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/VirtualGPSSatalines.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/VirtualGPSSatalines.java new file mode 100644 index 0000000..de216c8 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/location/VirtualGPSSatalines.java @@ -0,0 +1,133 @@ +package com.xposed.hook.location; + +import java.util.ArrayList; +import java.util.List; + +public class VirtualGPSSatalines { + private static VirtualGPSSatalines INSTANCE; + private int mAlmanacMask; + private float[] mAzimuths; + private float[] mElevations; + private int mEphemerisMask; + private float[] mSnrs; + private int mUsedInFixMask; + private int[] pnrs; + private int[] prnWithFlags; + private int svCount; + + static { + INSTANCE = new VirtualGPSSatalines(); + } + + public int getAlmanacMask() { + return this.mAlmanacMask; + } + + public float[] getAzimuths() { + return this.mAzimuths; + } + + public float[] getElevations() { + return this.mElevations; + } + + public int getEphemerisMask() { + return this.mEphemerisMask; + } + + public int[] getPrns() { + return this.pnrs; + } + + public float[] getSnrs() { + return this.mSnrs; + } + + public int getUsedInFixMask() { + return this.mUsedInFixMask; + } + + public static VirtualGPSSatalines get() { + return INSTANCE; + } + + private VirtualGPSSatalines() { + List statelines = new ArrayList<>(); + statelines.add(new GPSStateline(5, 1.0d, 5.0d, 112.0d, false, true, true)); + statelines.add(new GPSStateline(13, 13.5d, 23.0d, 53.0d, true, true, true)); + statelines.add(new GPSStateline(14, 19.1d, 6.0d, 247.0d, true, true, true)); + statelines.add(new GPSStateline(15, 31.0d, 58.0d, 45.0d, true, true, true)); + statelines.add(new GPSStateline(18, 0.0d, 52.0d, 309.0d, false, true, true)); + statelines.add(new GPSStateline(20, 30.1d, 54.0d, 105.0d, true, true, true)); + statelines.add(new GPSStateline(21, 33.2d, 56.0d, 251.0d, true, true, true)); + statelines.add(new GPSStateline(22, 0.0d, 14.0d, 299.0d, false, true, true)); + statelines.add(new GPSStateline(24, 25.9d, 57.0d, 157.0d, true, true, true)); + statelines.add(new GPSStateline(27, 18.0d, 3.0d, 309.0d, true, true, true)); + statelines.add(new GPSStateline(28, 18.2d, 3.0d, 42.0d, true, true, true)); + statelines.add(new GPSStateline(41, 28.8d, 0.0d, 0.0d, false, false, false)); + statelines.add(new GPSStateline(50, 29.2d, 0.0d, 0.0d, false, true, true)); + statelines.add(new GPSStateline(67, 14.4d, 2.0d, 92.0d, false, false, false)); + statelines.add(new GPSStateline(68, 21.2d, 45.0d, 60.0d, false, false, false)); + statelines.add(new GPSStateline(69, 17.5d, 50.0d, 330.0d, false, true, true)); + statelines.add(new GPSStateline(70, 22.4d, 7.0d, 291.0d, false, false, false)); + statelines.add(new GPSStateline(77, 23.8d, 10.0d, 23.0d, true, true, true)); + statelines.add(new GPSStateline(78, 18.0d, 47.0d, 70.0d, true, true, true)); + statelines.add(new GPSStateline(79, 22.8d, 41.0d, 142.0d, true, true, true)); + statelines.add(new GPSStateline(83, 0.2d, 9.0d, 212.0d, false, false, false)); + statelines.add(new GPSStateline(84, 16.7d, 30.0d, 264.0d, true, true, true)); + statelines.add(new GPSStateline(85, 12.1d, 20.0d, 317.0d, true, true, true)); + this.svCount = statelines.size(); + this.pnrs = new int[statelines.size()]; + for (int i = 0; i < statelines.size(); i++) { + this.pnrs[i] = statelines.get(i).getPnr(); + } + this.mSnrs = new float[statelines.size()]; + for (int i = 0; i < statelines.size(); i++) { + this.mSnrs[i] = (float) statelines.get(i).getSnr(); + } + this.mElevations = new float[statelines.size()]; + for (int i = 0; i < statelines.size(); i++) { + this.mElevations[i] = (float) statelines.get(i).getElevation(); + } + this.mAzimuths = new float[statelines.size()]; + for (int i = 0; i < statelines.size(); i++) { + this.mAzimuths[i] = (float) statelines.get(i).getAzimuth(); + } + this.mEphemerisMask = 0; + for (int i = 0; i < statelines.size(); i++) { + if (statelines.get(i).isHasEphemeris()) { + this.mEphemerisMask |= 1 << (statelines.get(i).getPnr() - 1); + } + } + this.mAlmanacMask = 0; + for (int i = 0; i < statelines.size(); i++) { + if (statelines.get(i).isHasAlmanac()) { + this.mAlmanacMask |= 1 << (statelines.get(i).getPnr() - 1); + } + } + this.mUsedInFixMask = 0; + for (int i = 0; statelines.size() > i; i++) { + if (statelines.get(i).isUseInFix()) { + this.mUsedInFixMask |= 1 << (statelines.get(i).getPnr() - 1); + } + } + this.prnWithFlags = new int[statelines.size()]; + for (int i = 0; i < statelines.size(); i++) { + GPSStateline gpsStateline = statelines.get(i); + this.prnWithFlags[i] = + (gpsStateline.isHasEphemeris() ? 1 : 0) + | (gpsStateline.isHasAlmanac() ? 1 : 0) << 1 + | (gpsStateline.isUseInFix() ? 1 : 0) << 2 + | 8 + | (gpsStateline.getPnr() << 7); + } + } + + public int getSvCount() { + return this.svCount; + } + + public int[] getPrnWithFlags() { + return this.prnWithFlags; + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/AppUtil.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/AppUtil.java new file mode 100644 index 0000000..b8b2a49 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/AppUtil.java @@ -0,0 +1,33 @@ +package com.xposed.hook.utils; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; + +import com.xposed.hook.config.PkgConfig; +import com.xposed.hook.entity.AppInfo; + +import java.util.ArrayList; +import java.util.List; + +public class AppUtil { + + public static ArrayList getAppList(Context context) { + ArrayList apps = new ArrayList<>(); + List installedPackages = context.getPackageManager().getInstalledPackages(0); + for (PackageInfo installedPackage : installedPackages) { + if ((installedPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + AppInfo app = new AppInfo(); + app.packageName = installedPackage.packageName; + app.title = installedPackage.applicationInfo.loadLabel(context.getPackageManager()).toString(); + app.icon = installedPackage.applicationInfo.loadIcon(context.getPackageManager()); + if (PkgConfig.pkg_dingtalk.equals(app.packageName)) + apps.add(0, app); + else + apps.add(app); + } + } + + return apps; + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/Tag.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/Tag.java new file mode 100644 index 0000000..4db490e --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/Tag.java @@ -0,0 +1,90 @@ +package com.xposed.hook.utils; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Created by lin on 2018/9/18. + */ +public class Tag { + + private String mPath; + private String mName; + private ArrayList mChildren = new ArrayList<>(); + private String mContent; + + Tag(String path, String name) { + mPath = path; + mName = name; + } + + void addChild(Tag tag) { + mChildren.add(tag); + } + + void setContent(String content) { + boolean hasContent = false; + if (content != null) { + for (int i = 0; i < content.length(); ++i) { + char c = content.charAt(i); + if ((c != ' ') && (c != '\n')) { + hasContent = true; + break; + } + } + } + if (hasContent) { + mContent = content; + } + } + + String getName() { + return mName; + } + + String getContent() { + return mContent; + } + + ArrayList getChildren() { + return mChildren; + } + + boolean hasChildren() { + return (mChildren.size() > 0); + } + + int getChildrenCount() { + return mChildren.size(); + } + + Tag getChild(int index) { + if ((index >= 0) && (index < mChildren.size())) { + return mChildren.get(index); + } + return null; + } + + HashMap> getGroupedElements() { + HashMap> groups = new HashMap<>(); + for (Tag child : mChildren) { + String key = child.getName(); + ArrayList group = groups.get(key); + if (group == null) { + group = new ArrayList<>(); + groups.put(key, group); + } + group.add(child); + } + return groups; + } + + String getPath() { + return mPath; + } + + @Override + public String toString() { + return "Tag: " + mName + ", " + mChildren.size() + " children, Content: " + mContent; + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/ViewHolder.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/ViewHolder.java new file mode 100644 index 0000000..fa3b2a2 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/ViewHolder.java @@ -0,0 +1,25 @@ +package com.xposed.hook.utils; + +import android.util.SparseArray; +import android.view.View; + +/** + * Created by lin on 2018/1/24. + */ + +public class ViewHolder { + + public static T get(View view, int id) { + SparseArray viewHolder = (SparseArray) view.getTag(); + if (viewHolder == null) { + viewHolder = new SparseArray(); + view.setTag(viewHolder); + } + View childView = viewHolder.get(id); + if (childView == null) { + childView = view.findViewById(id); + viewHolder.put(id, childView); + } + return (T) childView; + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/XmlToJson.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/XmlToJson.java new file mode 100644 index 0000000..41f75cf --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/utils/XmlToJson.java @@ -0,0 +1,433 @@ +package com.xposed.hook.utils; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.regex.Matcher; + +/** + * Created by lin on 2018/9/18. + */ +public class XmlToJson { + + private static final String TAG = "XmlToJson"; + private static final String DEFAULT_CONTENT_NAME = "content"; + private static final String DEFAULT_ENCODING = "utf-8"; + private static final String DEFAULT_INDENTATION = " "; + private String mIndentationPattern = DEFAULT_INDENTATION; + + // default values when a Tag is empty + private static final String DEFAULT_EMPTY_STRING = ""; + private static final int DEFAULT_EMPTY_INTEGER = 0; + private static final long DEFAULT_EMPTY_LONG = 0; + private static final double DEFAULT_EMPTY_DOUBLE = 0; + private static final boolean DEFAULT_EMPTY_BOOLEAN = false; + + + public static class Builder { + + private StringReader mStringSource; + private String mInputEncoding = DEFAULT_ENCODING; + private HashSet mForceListPaths = new HashSet<>(); + private HashMap mAttributeNameReplacements = new HashMap<>(); + private HashMap mContentNameReplacements = new HashMap<>(); + private HashMap mForceClassForPath = new HashMap<>(); // Integer, Long, Double, Boolean + private HashSet mSkippedAttributes = new HashSet<>(); + private HashSet mSkippedTags = new HashSet<>(); + + /** + * Constructor + * + * @param xmlSource XML source + */ + public Builder(@NonNull String xmlSource) { + mStringSource = new StringReader(xmlSource); + } + + /** + * Creates the XmlToJson object + */ + public JSONObject build() { + try { + return new JSONObject(new XmlToJson(this).toString()); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + } + + private StringReader mStringSource; + private InputStream mInputStreamSource; + private String mInputEncoding; + private HashSet mForceListPaths; + private HashMap mAttributeNameReplacements; + private HashMap mContentNameReplacements; + private HashMap mForceClassForPath; + private HashSet mSkippedAttributes = new HashSet<>(); + private HashSet mSkippedTags = new HashSet<>(); + private JSONObject mJsonObject; // Used for caching the result + + private XmlToJson(Builder builder) { + mStringSource = builder.mStringSource; + mInputEncoding = builder.mInputEncoding; + mForceListPaths = builder.mForceListPaths; + mAttributeNameReplacements = builder.mAttributeNameReplacements; + mContentNameReplacements = builder.mContentNameReplacements; + mForceClassForPath = builder.mForceClassForPath; + mSkippedAttributes = builder.mSkippedAttributes; + mSkippedTags = builder.mSkippedTags; + + mJsonObject = convertToJSONObject(); // Build now so that the InputStream can be closed just after + } + + + private + @Nullable + JSONObject convertToJSONObject() { + try { + Tag parentTag = new Tag("", "xml"); + + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(false); // tags with namespace are taken as-is ("namespace:tagname") + XmlPullParser xpp = factory.newPullParser(); + + setInput(xpp); + + int eventType = xpp.getEventType(); + while (eventType != XmlPullParser.START_DOCUMENT) { + eventType = xpp.next(); + } + readTags(parentTag, xpp); + + unsetInput(); + + return convertTagToJson(parentTag, false); + } catch (XmlPullParserException | IOException e) { + e.printStackTrace(); + return null; + } + } + + private void setInput(XmlPullParser xpp) { + if (mStringSource != null) { + try { + xpp.setInput(mStringSource); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + } else { + try { + xpp.setInput(mInputStreamSource, mInputEncoding); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + } + } + + private void unsetInput() { + if (mStringSource != null) { + mStringSource.close(); + } + // else the InputStream has been given by the user, it is not our role to close it + } + + private void readTags(Tag parent, XmlPullParser xpp) { + try { + int eventType; + do { + eventType = xpp.next(); + if (eventType == XmlPullParser.START_TAG) { + String tagName = xpp.getName(); + String path = parent.getPath() + "/" + tagName; + + boolean skipTag = mSkippedTags.contains(path); + + Tag child = new Tag(path, tagName); + if (!skipTag) { + parent.addChild(child); + } + + // Attributes are taken into account as key/values in the child + int attrCount = xpp.getAttributeCount(); + for (int i = 0; i < attrCount; ++i) { + String attrName = xpp.getAttributeName(i); + String attrValue = xpp.getAttributeValue(i); + String attrPath = parent.getPath() + "/" + child.getName() + "/" + attrName; + + // Skip Attributes + if (mSkippedAttributes.contains(attrPath)) { + continue; + } + + attrName = getAttributeNameReplacement(attrPath, attrName); + Tag attribute = new Tag(attrPath, attrName); + attribute.setContent(attrValue); + child.addChild(attribute); + } + + readTags(child, xpp); + } else if (eventType == XmlPullParser.TEXT) { + String text = xpp.getText(); + parent.setContent(text); + } else if (eventType == XmlPullParser.END_TAG) { + return; + } else { + Log.i(TAG, "unknown xml eventType " + eventType); + } + } while (eventType != XmlPullParser.END_DOCUMENT); + } catch (XmlPullParserException | IOException | NullPointerException e) { + e.printStackTrace(); + } + } + + private JSONObject convertTagToJson(Tag tag, boolean isListElement) { + JSONObject json = new JSONObject(); + + // Content is injected as a key/value + if (tag.getContent() != null) { + String path = tag.getPath(); + String name = getContentNameReplacement(path, DEFAULT_CONTENT_NAME); + putContent(path, json, name, tag.getContent()); + } + + try { + + HashMap> groups = tag.getGroupedElements(); // groups by tag names so that we can detect lists or single elements + for (ArrayList group : groups.values()) { + + if (group.size() == 1) { // element, or list of 1 + Tag child = group.get(0); + if (isForcedList(child)) { // list of 1 + JSONArray list = new JSONArray(); + list.put(convertTagToJson(child, true)); + String childrenNames = child.getName(); + json.put(childrenNames, list); + } else { // stand alone element + if (child.hasChildren()) { + JSONObject jsonChild = convertTagToJson(child, false); + json.put(child.getName(), jsonChild); + } else { + String path = child.getPath(); + putContent(path, json, child.getName(), child.getContent()); + } + } + } else { // list + JSONArray list = new JSONArray(); + for (Tag child : group) { + list.put(convertTagToJson(child, true)); + } + String childrenNames = group.get(0).getName(); + json.put(childrenNames, list); + } + } + return json; + + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + private void putContent(String path, JSONObject json, String tag, String content) { + try { + // checks if the user wants to force a class (Int, Double... for a given path) + Class forcedClass = mForceClassForPath.get(path); + if (forcedClass == null) { // default behaviour, put it as a String + if (content == null) { + content = DEFAULT_EMPTY_STRING; + } + json.put(tag, content); + } else { + if (forcedClass == Integer.class) { + try { + Integer number = Integer.parseInt(content); + json.put(tag, number); + } catch (NumberFormatException exception) { + json.put(tag, DEFAULT_EMPTY_INTEGER); + } + } else if (forcedClass == Long.class) { + try { + Long number = Long.parseLong(content); + json.put(tag, number); + } catch (NumberFormatException exception) { + json.put(tag, DEFAULT_EMPTY_LONG); + } + } else if (forcedClass == Double.class) { + try { + Double number = Double.parseDouble(content); + json.put(tag, number); + } catch (NumberFormatException exception) { + json.put(tag, DEFAULT_EMPTY_DOUBLE); + } + } else if (forcedClass == Boolean.class) { + if (content == null) { + json.put(tag, DEFAULT_EMPTY_BOOLEAN); + } else if (content.equalsIgnoreCase("true")) { + json.put(tag, true); + } else if (content.equalsIgnoreCase("false")) { + json.put(tag, false); + } else { + json.put(tag, DEFAULT_EMPTY_BOOLEAN); + } + } + } + + } catch (JSONException exception) { + // keep continue in case of error + } + } + + private boolean isForcedList(Tag tag) { + String path = tag.getPath(); + return mForceListPaths.contains(path); + } + + private String getAttributeNameReplacement(String path, String defaultValue) { + String result = mAttributeNameReplacements.get(path); + if (result != null) { + return result; + } + return defaultValue; + } + + private String getContentNameReplacement(String path, String defaultValue) { + String result = mContentNameReplacements.get(path); + if (result != null) { + return result; + } + return defaultValue; + } + + @Override + public String toString() { + if (mJsonObject != null) { + return mJsonObject.toString(); + } + return null; + } + + /** + * Format the Json with indentation and line breaks. + * Uses the last intendation pattern used, or the default one (3 spaces) + * + * @return the Builder + */ + public String toFormattedString() { + if (mJsonObject != null) { + String indent = ""; + StringBuilder builder = new StringBuilder(); + builder.append("{\n"); + format(mJsonObject, builder, indent); + builder.append("}\n"); + return builder.toString(); + } + return null; + } + + private void format(JSONObject jsonObject, StringBuilder builder, String indent) { + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + builder.append(indent); + builder.append(mIndentationPattern); + builder.append("\""); + builder.append(key); + builder.append("\": "); + Object value = jsonObject.opt(key); + if (value instanceof JSONObject) { + JSONObject child = (JSONObject) value; + builder.append(indent); + builder.append("{\n"); + format(child, builder, indent + mIndentationPattern); + builder.append(indent); + builder.append(mIndentationPattern); + builder.append("}"); + } else if (value instanceof JSONArray) { + JSONArray array = (JSONArray) value; + formatArray(array, builder, indent + mIndentationPattern); + } else { + formatValue(value, builder); + } + if (keys.hasNext()) { + builder.append(",\n"); + } else { + builder.append("\n"); + } + } + } + + private void formatArray(JSONArray array, StringBuilder builder, String indent) { + builder.append("[\n"); + + for (int i = 0; i < array.length(); ++i) { + Object element = array.opt(i); + if (element instanceof JSONObject) { + JSONObject child = (JSONObject) element; + builder.append(indent); + builder.append(mIndentationPattern); + builder.append("{\n"); + format(child, builder, indent + mIndentationPattern); + builder.append(indent); + builder.append(mIndentationPattern); + builder.append("}"); + } else if (element instanceof JSONArray) { + JSONArray child = (JSONArray) element; + formatArray(child, builder, indent + mIndentationPattern); + } else { + formatValue(element, builder); + } + if (i < array.length() - 1) { + builder.append(","); + } + builder.append("\n"); + } + builder.append(indent); + builder.append("]"); + } + + private void formatValue(Object value, StringBuilder builder) { + if (value instanceof String) { + String string = (String) value; + + // Escape special characters + string = string.replaceAll("\\\\", "\\\\\\\\"); // escape backslash + string = string.replaceAll("\"", Matcher.quoteReplacement("\\\"")); // escape double quotes + string = string.replaceAll("/", "\\\\/"); // escape slash + string = string.replaceAll("\n", "\\\\n").replaceAll("\t", "\\\\t"); // escape \n and \t + + builder.append("\""); + builder.append(string); + builder.append("\""); + } else if (value instanceof Long) { + Long longValue = (Long) value; + builder.append(longValue); + } else if (value instanceof Integer) { + Integer intValue = (Integer) value; + builder.append(intValue); + } else if (value instanceof Boolean) { + Boolean bool = (Boolean) value; + builder.append(bool); + } else if (value instanceof Double) { + Double db = (Double) value; + builder.append(db); + } else { + builder.append(value.toString()); + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/LuckyMoneyHook.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/LuckyMoneyHook.java new file mode 100644 index 0000000..cfe4a0b --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/LuckyMoneyHook.java @@ -0,0 +1,206 @@ +package com.xposed.hook.wechat; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.text.TextUtils; +import android.util.Log; +import android.widget.Button; +import android.widget.Toast; + +import com.xposed.hook.location.LocationHook; +import com.xposed.hook.utils.XmlToJson; + +import org.json.JSONObject; + +import java.lang.ref.WeakReference; +import java.util.HashSet; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XSharedPreferences; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; +import de.robv.android.xposed.callbacks.XC_LoadPackage; + +/** + * Created by lin on 2018/2/2. + */ + +public class LuckyMoneyHook { + + public static final String WECHAT_PACKAGE_NAME = "com.tencent.mm"; + + private static final String tinkerEnableClass = "com.tencent.tinker.loader.shareutil.ShareTinkerInternals"; + private static final String tinkerEnableMethodName = "abV"; + + private static final String luckyMoneyReceiveUI = WECHAT_PACKAGE_NAME + ".plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI"; + private static final String receiveUIFunctionName = "onSceneEnd"; + private static final String receiveUIParamName = WECHAT_PACKAGE_NAME + ".al.n"; + + private static final String chatRoomInfoUI = WECHAT_PACKAGE_NAME + ".chatroom.ui.ChatroomInfoUI"; + private static final String launcherUI = WECHAT_PACKAGE_NAME + ".ui.LauncherUI"; + private static final String openUIClass = WECHAT_PACKAGE_NAME + ".bs.d";//MicroMsg.PluginHelper + private static final String openUIMethodName = "b"; + + private static HashSet autoReceiveIds = new HashSet<>(); + private static WeakReference launcherUiActivity; + + private static ToastHandler handler; + + private static long msgId; + private static int delay; + + public static void hook(final XC_LoadPackage.LoadPackageParam mLpp) { + if (WECHAT_PACKAGE_NAME.equals(mLpp.packageName)) { + disableTinker(mLpp); + XSharedPreferences preferences = new XSharedPreferences("com.xposed.hook", "lucky_money"); + delay = preferences.getInt("lucky_money_delay", 0); + try { + XposedHelpers.findAndHookMethod("android.app.Application", mLpp.classLoader, "attach", Context.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + Context context = (Context) param.args[0]; + handler = new ToastHandler(context); + } + }); + if (preferences.getBoolean("quick_open", true)) + XposedHelpers.findAndHookMethod(luckyMoneyReceiveUI, mLpp.classLoader, receiveUIFunctionName, int.class, int.class, String.class, receiveUIParamName, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + try { + Button button = (Button) XposedHelpers.findFirstFieldByExactType(param.thisObject.getClass(), Button.class).get(param.thisObject); + if (button.isShown() && button.isClickable()) { + button.performClick(); + } + } catch (Throwable e) { + Log.e(LocationHook.TAG, e.toString()); + } + } + }); + if (preferences.getBoolean("auto_receive", true)) { + XposedHelpers.findAndHookMethod(WechatUnrecalledHook.SQLiteDatabaseClass, mLpp.classLoader, "insert", String.class, String.class, ContentValues.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + ContentValues contentValues = (ContentValues) param.args[2]; + String tableName = (String) param.args[0]; + if (TextUtils.isEmpty(tableName) || !tableName.equals("message")) { + return; + } + Integer type = contentValues.getAsInteger("type"); + if (null == type) { + return; + } + Long id = contentValues.getAsLong("msgId"); + if (id != null) { + if (id == msgId) + XposedBridge.log("wechat msg:" + contentValues.getAsString("content")); + msgId = id; + } + if (handler != null && (type == 436207665 || type == 469762097)) { + handler.obtainMessage(0, "Lucky Money is Coming").sendToTarget(); + openLuckyMoneyReceiveUI(contentValues, mLpp); + } + } + }); + XposedHelpers.findAndHookMethod(chatRoomInfoUI, mLpp.classLoader, "onCreate", Bundle.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + if (handler != null) { + Activity activity = (Activity) param.thisObject; + String wechatId = activity.getIntent().getStringExtra("RoomInfo_Id"); + String status = "Opened"; + if (autoReceiveIds.contains(wechatId)) { + autoReceiveIds.remove(wechatId); + status = "Closed"; + } else + autoReceiveIds.add(wechatId); + handler.obtainMessage(0, "Group Chat ID:" + wechatId + ",Auto Open LuckyMoneyReceiveUI " + status).sendToTarget(); + } + } + }); + XposedHelpers.findAndHookMethod(launcherUI, mLpp.classLoader, "onCreate", Bundle.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + launcherUiActivity = new WeakReference<>((Activity) param.thisObject); + } + }); + } + } catch (Throwable e) { + XposedBridge.log(e); + } + if (preferences.getBoolean("recalled", true)) + new WechatUnrecalledHook(WECHAT_PACKAGE_NAME).hook(mLpp.classLoader); + if (preferences.getBoolean("3_days_Moments", false)) + WechatUnrecalledHook.hook3DaysMoments(mLpp.classLoader); + } + } + + private static void openLuckyMoneyReceiveUI(ContentValues contentValues, XC_LoadPackage.LoadPackageParam lpparam) { + int status = contentValues.getAsInteger("status"); + if (status == 4) + return; + + String talker = contentValues.getAsString("talker"); + if (!autoReceiveIds.contains(talker)) + return; + + String content = contentValues.getAsString("content"); + if (!content.startsWith(" { + try { + Intent param = new Intent(); + param.putExtra("key_way", 1); + param.putExtra("key_native_url", nativeUrlString); + param.putExtra("key_username", talker); + XposedHelpers.callStaticMethod(XposedHelpers.findClass(openUIClass, lpparam.classLoader), + openUIMethodName, launcherUiActivity.get(), "luckymoney", ".ui.LuckyMoneyNotHookReceiveUI", param); + } catch (Throwable e) { + XposedBridge.log(e); + } + }, delay); + } + } catch (Throwable e) { + XposedBridge.log(e); + } + } + + private static void disableTinker(XC_LoadPackage.LoadPackageParam lpparam) { + try { + XposedHelpers.findAndHookMethod(tinkerEnableClass, lpparam.classLoader, tinkerEnableMethodName, int.class, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + param.setResult(false); + } + }); + } catch (Throwable e) { + XposedBridge.log(e); + } + } + + private static class ToastHandler extends Handler { + + private Context context; + + ToastHandler(Context context) { + super(Looper.getMainLooper()); + this.context = context; + } + + @Override + public void handleMessage(Message msg) { + Toast.makeText(context, (String) msg.obj, Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/WechatMainDBHelper.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/WechatMainDBHelper.java new file mode 100644 index 0000000..2ad76a1 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/WechatMainDBHelper.java @@ -0,0 +1,194 @@ +package com.xposed.hook.wechat; + +import android.content.ContentValues; +import android.database.Cursor; +import android.text.TextUtils; + +import java.util.HashMap; +import java.util.Random; + +import static de.robv.android.xposed.XposedHelpers.callMethod; + +/** + * Created by lin on 2018/2/6. + */ + +public class WechatMainDBHelper { + private Object SQLDB; + private HashMap mNicknameCache; + private HashMap mChatroomMemberMap; + + public WechatMainDBHelper(Object dbObject) { + SQLDB = dbObject; + mNicknameCache = new HashMap<>(); + } + + public void insertSQL(String table, String selection, ContentValues contentValues) { + callMethod(SQLDB, "insert", table, selection, contentValues); + } + + public Cursor rawQuery(String query) { + return rawQuery(query, null); + } + + public Cursor rawQuery(String query, String[] args) { + return (Cursor) callMethod(SQLDB, "rawQuery", query, args); + } + + public void SQLUpdate(String table, ContentValues contentValues, String selection, String[] args) { + callMethod(SQLDB, "update", table, contentValues, selection, args); + } + + public Cursor getMessageBySvrId(String msgSrvId) { + String sql = "select * from message where msgsvrid=?"; + String[] sqlArgs = {msgSrvId}; + + return rawQuery(sql, sqlArgs); + } + + public void insertMessage(String talker, int talkerId, String msg) { + insertMessage(talker, talkerId, msg, 1, System.currentTimeMillis()); + } + + public void insertSystemMessage(String talker, int talkerId, String msg) { + insertMessage(talker, talkerId, msg, 10000, System.currentTimeMillis()); + } + + public void insertSystemMessage(String talker, int talkerId, String msg, long createTime) { + insertMessage(talker, talkerId, msg, 10000, createTime); + } + + public void insertMessage(String talker, int talkerId, String msg, int type, long createTime) { + int status = 3; + long msgSvrId = createTime + (new Random().nextInt()); + long msgId = getNextMsgId(); + ContentValues v = new ContentValues(); + v.put("msgId", msgId); + v.put("msgSvrid", msgSvrId); + v.put("type", type); + v.put("status", status); + v.put("createTime", createTime); + v.put("talker", talker); + v.put("content", msg); + if (talkerId != -1) { + v.put("talkerid", talkerId); + } + insertSQL("message", "", v); + } + + public long getNextMsgId() { + Cursor cursor = rawQuery("SELECT max(msgId) FROM message"); + if (cursor == null || !cursor.moveToFirst()) + return -1; + + long id = cursor.getInt(0) + 1; + cursor.close(); + return id; + } + + public Cursor getLastMsg(String username) { + String query = "SELECT * FROM message WHERE msgId = (SELECT max(msgId) FROM message WHERE talker='" + + username + "')"; + return rawQuery(query); + } + + public int getUnreadCount(String username) { + Cursor cursor = rawQuery("select unReadCount from rconversation where " + + "username = '" + username + + "' and ( parentref is null or parentref = '' ) "); + + if (cursor == null || !cursor.moveToFirst()) + return 0; + + int cnt = cursor.getInt(cursor.getColumnIndex("unReadCount")); + cursor.close(); + return cnt; + } + + public String getNickname(String username) { + if (mNicknameCache.containsKey(username)) { + return mNicknameCache.get(username); + } + + Cursor cursor = getContact(username); + if (cursor == null || !cursor.moveToFirst()) + return username; + + String name = cursor.getString(cursor.getColumnIndex("conRemark")); + if (TextUtils.isEmpty(name)) { + name = cursor.getString(cursor.getColumnIndex("nickname")); + } + name = name.trim(); + cursor.close(); + mNicknameCache.put(username, name); + return name; + } + + public HashMap getChatRoomMembers() { + String query = "SELECT * FROM chatroom"; + Cursor cursor = rawQuery(query); + + HashMap map = new HashMap<>(); + + if (cursor == null || !cursor.moveToFirst()) + return map; + + do { + String memberlist = cursor.getString(cursor.getColumnIndex("memberlist")); + String displayname = cursor.getString(cursor.getColumnIndex("displayname")); + + String[] members = memberlist.split(";"); + String[] names; + if (displayname.contains("、")) { + names = displayname.split("、"); + } else { + names = displayname.split(","); + } + + for (int i = 0; i < members.length; i++) { + map.put(members[i].trim(), names[i].trim()); + } + + } while (cursor.moveToNext()); + cursor.close(); + + return map; + } + + public String getChatroomName(String username) { + String name = getNickname(username); + if (!TextUtils.isEmpty(name)) + return name; + + String query = "SELECT * FROM chatroom WHERE chatroomname = ?"; + Cursor cursor = rawQuery(query, new String[]{username}); + if (cursor == null || !cursor.moveToFirst()) + return null; + + name = cursor.getString(cursor.getColumnIndex("displayname")); + cursor.close(); + return name; + } + + public String getChatroomMemberName(String username) { + if (mChatroomMemberMap == null) { + mChatroomMemberMap = getChatRoomMembers(); + } + + if (mChatroomMemberMap.containsKey(username)) { + return mChatroomMemberMap.get(username); + } + + // reload + mChatroomMemberMap = getChatRoomMembers(); + if (mChatroomMemberMap.containsKey(username)) { + return mChatroomMemberMap.get(username); + } + return null; + } + + public Cursor getContact(String username) { + String query = "SELECT * FROM rcontact WHERE username = ?"; + return rawQuery(query, new String[]{username}); + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/WechatUnrecalledHook.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/WechatUnrecalledHook.java new file mode 100644 index 0000000..8c5cbc8 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/com/xposed/hook/wechat/WechatUnrecalledHook.java @@ -0,0 +1,286 @@ +package com.xposed.hook.wechat; + +import android.content.ContentValues; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; + +import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; +import static de.robv.android.xposed.XposedHelpers.findClass; + +/** + * Created by lin on 2018/2/6. + */ + +public class WechatUnrecalledHook { + + private static final int EXEC_SUC = 1; + + static final String SQLiteDatabaseClass = "com.tencent.wcdb.database.SQLiteDatabase"; + + private static final String recallClass = LuckyMoneyHook.WECHAT_PACKAGE_NAME + ".sdk.platformtools.bw"; + private static final String recallMethod = "S"; + private static final String storageClass = LuckyMoneyHook.WECHAT_PACKAGE_NAME + ".storage.w"; + private static final String storageMethodParam = LuckyMoneyHook.WECHAT_PACKAGE_NAME + ".sdk.e.e"; + private static final String incMsgLocalIdClass = LuckyMoneyHook.WECHAT_PACKAGE_NAME + ".storage.bl"; + private static final String incMsgLocalIdMethod = "aHd"; + private static final String updateMsgLocalIdMethod = "ao"; + private static final String updateMsgLocalIdMethodParam = LuckyMoneyHook.WECHAT_PACKAGE_NAME + ".storage.bk"; + + private static final boolean mDebug = true; + private WechatMainDBHelper mDb; + private Object mObject; + private Object updateMsgLocalIdMethodParamObj; + + private Map mSettings = new HashMap<>(); + + WechatUnrecalledHook(String packageName) { + mSettings.put("prevent_moments_recall", true); + mSettings.put("prevent_comments_recall", true); + } + + private static void findAndHookConstructor(String className, ClassLoader classLoader, Object... parameters) { + Class cls = findClass(className, classLoader); + Class[] parameterTypes = new Class[parameters.length - 1]; + for (int i = 0; i < parameters.length - 1; i++) { + if (parameters[i] instanceof String) { + parameterTypes[i] = findClass((String) parameters[i], classLoader); + } else if (parameters[i] instanceof Class) { + parameterTypes[i] = (Class) parameters[i]; + } + } + try { + Constructor constructor = cls.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + XC_MethodHook callback = (XC_MethodHook) parameters[parameters.length - 1]; + XposedBridge.hookMethod(constructor, callback); + } catch (Throwable t) { + XposedBridge.log(t); + } + } + + public void hook(final ClassLoader loader) { + try { + hookRecall(loader); + } catch (Throwable e) { + XposedBridge.log(e); + } + try { + hookDatabase(loader); + } catch (Throwable e) { + XposedBridge.log(e); + } + try { + hookDbObject(loader); + } catch (Throwable t) { + XposedBridge.log(t); + } + try { + hookMsgLocalId(loader); + } catch (Throwable t) { + XposedBridge.log(t); + } + } + + private void hookRecall(final ClassLoader loader) { + findAndHookMethod(recallClass, loader, + recallMethod, String.class, String.class, + new XC_MethodHook() { + @SuppressWarnings("unchecked") + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + preventMsgRecall(param); + } + }); + } + + private void hookDatabase(ClassLoader loader) { + findAndHookMethod(SQLiteDatabaseClass, loader, + "updateWithOnConflict", String.class, ContentValues.class, String.class, + String[].class, int.class, + new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + preventCommentRecall(param); + preventMomentRecall(param); + } + }); + + findAndHookMethod(SQLiteDatabaseClass, loader, + "executeSql", String.class, Object[].class, "com.tencent.wcdb.support.CancellationSignal", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + String query = (String) param.args[0]; + if (mSettings.get("prevent_moments_recall") && + query.toLowerCase().contains("snsinfo set sourcetype")) { + XposedBridge.log("preventMomentRecall executeSql"); + param.setResult(EXEC_SUC); + } + } + }); + + } + + static void hook3DaysMoments(ClassLoader loader) { + findAndHookMethod(SQLiteDatabaseClass, loader, "rawQueryWithFactory", + SQLiteDatabaseClass + ".CursorFactory", String.class, Object[].class, String.class, "com.tencent.wcdb.support.CancellationSignal", + new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + Log.e("rawQueryWithFactory", param.args[1] + ":" + param.args[3]); + String sourceType = "sourceType in (8,72,10,74,12,76,14,78,24,88,26,90,28,92,30,94)"; + String type = "type in ( 1,2 , 3 , 4 , 18 , 5 , 12 , 9 , 14 , 15 , 13 , 21 , 25 , 26,28,29,30)"; + if (param.args[1] != null && param.args[1].toString().contains("from SnsInfo") && + param.args[1].toString().contains(sourceType) && + param.args[1].toString().contains(type)) { + param.args[1] = param.args[1].toString().replace(sourceType, "1=1") + .replace(type, "1=1") + .replace("snsId >=", "0 !="); + } + } + + }); + } + + private void hookDbObject(final ClassLoader loader) { + // get database object + findAndHookConstructor(storageClass, loader, + storageMethodParam, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + // look for: LinkedBlockingQueue + if (mDb == null) { + try { + mDb = new WechatMainDBHelper(param.args[0]); + } catch (Throwable t) { + log(t); + } + } + } + }); + } + + private void hookMsgLocalId(ClassLoader loader) { + findAndHookMethod(incMsgLocalIdClass, loader, incMsgLocalIdMethod, String.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + if ("message".equals(param.args[0])) + mObject = param.getResult(); + } + }); + try { + Class cls = XposedHelpers.findClass(updateMsgLocalIdMethodParam, loader); + updateMsgLocalIdMethodParamObj = cls.newInstance(); + } catch (Throwable e) { + XposedBridge.log(e); + } + } + + private void preventMsgRecall(XC_MethodHook.MethodHookParam param) { + String xml = (String) param.args[0]; + String tag = (String) param.args[1]; + if (TextUtils.isEmpty(xml) || TextUtils.isEmpty(tag) || + !tag.equals("sysmsg") || !xml.contains("revokemsg")) { + return; + } + + @SuppressWarnings("unchecked") Map map = + (Map) param.getResult(); + if (map == null) + return; + + String key = ".sysmsg.$type"; + if (!map.containsKey(key)) + return; + + String type = map.get(key); + if (type == null || !type.equals("revokemsg")) + return; + + final String talker = map.get(".sysmsg.revokemsg.session"); + String replacemsg = map.get(".sysmsg.revokemsg.replacemsg"); + String msgsvrid = map.get(".sysmsg.revokemsg.newmsgid"); + + if (replacemsg.startsWith("你") || replacemsg.toLowerCase().startsWith("you")) { + return; + } + + String[] strings = replacemsg.split("\""); + replacemsg = "\"" + strings[1] + "\" " + "尝试撤回上一条消息 (已阻止)"; + + map.put(key, null); + param.setResult(map); + + try { + Cursor cursor = mDb.getMessageBySvrId(msgsvrid); + if (cursor == null || !cursor.moveToFirst()) + return; + + long createTime = cursor.getLong(cursor.getColumnIndex("createTime")); + int idx = cursor.getColumnIndex("talkerId"); + int talkerId = -1; + if (idx != -1) { + talkerId = cursor.getInt(cursor.getColumnIndex("talkerId")); + } + cursor.close(); + mDb.insertSystemMessage(talker, talkerId, replacemsg, createTime + 1); + updateMessageCount(); + } catch (Throwable t) { + XposedBridge.log(t); + } + + } + + private void updateMessageCount() { + if (mObject != null) { + XposedHelpers.callMethod(mObject, updateMsgLocalIdMethod, updateMsgLocalIdMethodParamObj); + XposedBridge.log("updateMessageCount"); + } + } + + private void preventCommentRecall(XC_MethodHook.MethodHookParam param) { + String table = (String) param.args[0]; + if (!table.equalsIgnoreCase("snscomment")) + return; + + ContentValues v = (ContentValues) param.args[1]; + if (v.containsKey("commentflag") && v.getAsInteger("commentflag") == 1 && + mSettings.get("prevent_comments_recall")) { + XposedBridge.log("preventCommentRecall"); + param.setResult(EXEC_SUC); // prevent call + } + } + + private void preventMomentRecall(XC_MethodHook.MethodHookParam param) { + String table = (String) param.args[0]; + if (!table.equalsIgnoreCase("snsinfo")) + return; + + ContentValues v = (ContentValues) param.args[1]; + if (mSettings.get("prevent_moments_recall") && + v.containsKey("sourceType") && v.containsKey("type")) { + int sourceType = v.getAsInteger("sourceType"); + int type = v.getAsInteger("type"); + //type: 2: text, 21 luckymoneyphoto, + if (sourceType == 0 || (type != 2 && sourceType == 8/*set to private*/)) { + XposedBridge.log("preventMomentRecall"); + param.setResult(EXEC_SUC); // prevent call + } + } + } + + private void log(Throwable t) { + if (mDebug) { + XposedBridge.log(t); + } + } + +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/MethodParams.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/MethodParams.java new file mode 100644 index 0000000..4b8b8cb --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/MethodParams.java @@ -0,0 +1,12 @@ +package mirror; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodParams { + Class[] value(); +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/MethodReflectParams.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/MethodReflectParams.java new file mode 100644 index 0000000..d2f9b5a --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/MethodReflectParams.java @@ -0,0 +1,12 @@ +package mirror; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodReflectParams { + String[] value(); +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefBoolean.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefBoolean.java new file mode 100644 index 0000000..995af1d --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefBoolean.java @@ -0,0 +1,28 @@ +package mirror; + +import java.lang.reflect.Field; + +public class RefBoolean { + private Field field; + + public RefBoolean(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public boolean get(Object object) { + try { + return this.field.getBoolean(object); + } catch (Exception e) { + return false; + } + } + + public void set(Object obj, boolean value) { + try { + this.field.setBoolean(obj, value); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefClass.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefClass.java new file mode 100644 index 0000000..0d7dca6 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefClass.java @@ -0,0 +1,57 @@ +package mirror; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; + +public final class RefClass { + + private static HashMap,Constructor> REF_TYPES = new HashMap, Constructor>(); + static { + try { + REF_TYPES.put(RefObject.class, RefObject.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefMethod.class, RefMethod.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefInt.class, RefInt.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefLong.class, RefLong.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefFloat.class, RefFloat.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefDouble.class, RefDouble.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefBoolean.class, RefBoolean.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefStaticObject.class, RefStaticObject.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefStaticInt.class, RefStaticInt.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefStaticMethod.class, RefStaticMethod.class.getConstructor(Class.class, Field.class)); + REF_TYPES.put(RefConstructor.class, RefConstructor.class.getConstructor(Class.class, Field.class)); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public static Class load(Class mappingClass, String className) { + try { + return load(mappingClass, Class.forName(className)); + } catch (Exception e) { + return null; + } + } + + + public static Class load(Class mappingClass, Class realClass) { + Field[] fields = mappingClass.getDeclaredFields(); + for (Field field : fields) { + try { + if (Modifier.isStatic(field.getModifiers())) { + Constructor constructor = REF_TYPES.get(field.getType()); + if (constructor != null) { + field.set(null, constructor.newInstance(realClass, field)); + } + } + } + catch (Exception e) { + // Ignore + } + } + return realClass; + } + +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefConstructor.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefConstructor.java new file mode 100644 index 0000000..03736a3 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefConstructor.java @@ -0,0 +1,49 @@ +package mirror; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + +public class RefConstructor { + private Constructor ctor; + + public RefConstructor(Class cls, Field field) throws NoSuchMethodException { + if (field.isAnnotationPresent(MethodParams.class)) { + Class[] types = field.getAnnotation(MethodParams.class).value(); + ctor = cls.getDeclaredConstructor(types); + } else if (field.isAnnotationPresent(MethodReflectParams.class)) { + String[] values = field.getAnnotation(MethodReflectParams.class).value(); + Class[] parameterTypes = new Class[values.length]; + int N = 0; + while (N < values.length) { + try { + parameterTypes[N] = Class.forName(values[N]); + N++; + } catch (Exception e) { + e.printStackTrace(); + } + } + ctor = cls.getDeclaredConstructor(parameterTypes); + } else { + ctor = cls.getDeclaredConstructor(); + } + if (ctor != null && !ctor.isAccessible()) { + ctor.setAccessible(true); + } + } + + public T newInstance() { + try { + return (T) ctor.newInstance(); + } catch (Exception e) { + return null; + } + } + + public T newInstance(Object... params) { + try { + return (T) ctor.newInstance(params); + } catch (Exception e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefDouble.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefDouble.java new file mode 100644 index 0000000..0608c4c --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefDouble.java @@ -0,0 +1,28 @@ +package mirror; + +import java.lang.reflect.Field; + +public class RefDouble { + private Field field; + + public RefDouble(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public double get(Object object) { + try { + return this.field.getDouble(object); + } catch (Exception e) { + return 0; + } + } + + public void set(Object obj, double value) { + try { + this.field.setDouble(obj, value); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefFloat.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefFloat.java new file mode 100644 index 0000000..117383b --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefFloat.java @@ -0,0 +1,28 @@ +package mirror; + +import java.lang.reflect.Field; + +public class RefFloat { + private Field field; + + public RefFloat(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public float get(Object object) { + try { + return this.field.getFloat(object); + } catch (Exception e) { + return 0; + } + } + + public void set(Object obj, float value) { + try { + this.field.setFloat(obj, value); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefInt.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefInt.java new file mode 100644 index 0000000..6795ec2 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefInt.java @@ -0,0 +1,28 @@ +package mirror; + +import java.lang.reflect.Field; + +public class RefInt { + private Field field; + + public RefInt(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public int get(Object object) { + try { + return this.field.getInt(object); + } catch (Exception e) { + return 0; + } + } + + public void set(Object obj, int intValue) { + try { + this.field.setInt(obj, intValue); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefLong.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefLong.java new file mode 100644 index 0000000..ccdaeb9 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefLong.java @@ -0,0 +1,28 @@ +package mirror; + +import java.lang.reflect.Field; + +public class RefLong { + private Field field; + + public RefLong(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public long get(Object object) { + try { + return this.field.getLong(object); + } catch (Exception e) { + return 0; + } + } + + public void set(Object obj, long value) { + try { + this.field.setLong(obj, value); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefMethod.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefMethod.java new file mode 100644 index 0000000..77cb78e --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefMethod.java @@ -0,0 +1,90 @@ +package mirror; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static mirror.RefStaticMethod.getProtoType; + +@SuppressWarnings("unchecked") +public class RefMethod { + private Method method; + + public RefMethod(Class cls, Field field) throws NoSuchMethodException { + if (field.isAnnotationPresent(MethodParams.class)) { + Class[] types = field.getAnnotation(MethodParams.class).value(); + for (int i = 0; i < types.length; i++) { + Class clazz = types[i]; + if (clazz.getClassLoader() == getClass().getClassLoader()) { + try { + Class.forName(clazz.getName()); + Class realClass = (Class) clazz.getField("TYPE").get(null); + types[i] = realClass; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + } + this.method = cls.getDeclaredMethod(field.getName(), types); + this.method.setAccessible(true); + } else if (field.isAnnotationPresent(MethodReflectParams.class)) { + String[] typeNames = field.getAnnotation(MethodReflectParams.class).value(); + Class[] types = new Class[typeNames.length]; + for (int i = 0; i < typeNames.length; i++) { + Class type = getProtoType(typeNames[i]); + if (type == null) { + try { + type = Class.forName(typeNames[i]); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + types[i] = type; + } + this.method = cls.getDeclaredMethod(field.getName(), types); + this.method.setAccessible(true); + } + else { + for (Method method : cls.getDeclaredMethods()) { + if (method.getName().equals(field.getName())) { + this.method = method; + this.method.setAccessible(true); + break; + } + } + } + if (this.method == null) { + throw new NoSuchMethodException(field.getName()); + } + } + + public T call(Object receiver, Object... args) { + try { + return (T) this.method.invoke(receiver, args); + } catch (InvocationTargetException e) { + if (e.getCause() != null) { + e.getCause().printStackTrace(); + } else { + e.printStackTrace(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + + public T callWithException(Object receiver, Object... args) throws Throwable { + try { + return (T) this.method.invoke(receiver, args); + } catch (InvocationTargetException e) { + if (e.getCause() != null) { + throw e.getCause(); + } + throw e; + } + } + + public Class[] paramList() { + return method.getParameterTypes(); + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefObject.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefObject.java new file mode 100644 index 0000000..dcbf977 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefObject.java @@ -0,0 +1,29 @@ +package mirror; + +import java.lang.reflect.Field; + +@SuppressWarnings("unchecked") +public class RefObject { + private Field field; + + public RefObject(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public T get(Object object) { + try { + return (T) this.field.get(object); + } catch (Exception e) { + return null; + } + } + + public void set(Object obj, T value) { + try { + this.field.set(obj, value); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticInt.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticInt.java new file mode 100644 index 0000000..6617419 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticInt.java @@ -0,0 +1,28 @@ +package mirror; + +import java.lang.reflect.Field; + +public class RefStaticInt { + private Field field; + + public RefStaticInt(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public int get() { + try { + return this.field.getInt(null); + } catch (Exception e) { + return 0; + } + } + + public void set(int value) { + try { + this.field.setInt(null, value); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticMethod.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticMethod.java new file mode 100644 index 0000000..a4be897 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticMethod.java @@ -0,0 +1,136 @@ +package mirror; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +@SuppressWarnings("unchecked") +public class RefStaticMethod { + private Method method; + + public RefStaticMethod(Class cls, Field field) throws NoSuchMethodException { + if (field.isAnnotationPresent(MethodParams.class)) { + Class[] types = field.getAnnotation(MethodParams.class).value(); + for (int i = 0; i < types.length; i++) { + Class clazz = types[i]; + if (clazz.getClassLoader() == getClass().getClassLoader()) { + try { + Class.forName(clazz.getName()); + Class realClass = (Class) clazz.getField("TYPE").get(null); + types[i] = realClass; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + } + this.method = cls.getDeclaredMethod(field.getName(), types); + this.method.setAccessible(true); + } else if (field.isAnnotationPresent(MethodReflectParams.class)) { + boolean arrayset=false; + String[] typeNames = field.getAnnotation(MethodReflectParams.class).value(); + Class[] types = new Class[typeNames.length]; + Class[] types2 = new Class[typeNames.length]; + for (int i = 0; i < typeNames.length; i++) { + Class type = getProtoType(typeNames[i]); + if (type == null) { + try { + type = Class.forName(typeNames[i]); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + types[i] = type; + if("java.util.HashSet".equals(typeNames[i])){ + arrayset=true; + Class type2 =type; + try { + type2 = Class.forName("android.util.ArraySet"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + if(type2 != null) { + types2[i] = type2; + }else{ + types2[i] = type; + } + }else{ + types2[i] = type; + } + } + try { + this.method = cls.getDeclaredMethod(field.getName(), types); + }catch (Exception e){ + e.printStackTrace(); + if(arrayset){ + this.method = cls.getDeclaredMethod(field.getName(), types2); + } + } + this.method.setAccessible(true); + } else { + for (Method method : cls.getDeclaredMethods()) { + if (method.getName().equals(field.getName())) { + this.method = method; + this.method.setAccessible(true); + break; + } + } + } + + if (this.method == null) { + throw new NoSuchMethodException(field.getName()); + } + } + + static Class getProtoType(String typeName) { + if (typeName.equals("int")) { + return Integer.TYPE; + } + if (typeName.equals("long")) { + return Long.TYPE; + } + if (typeName.equals("boolean")) { + return Boolean.TYPE; + } + if (typeName.equals("byte")) { + return Byte.TYPE; + } + if (typeName.equals("short")) { + return Short.TYPE; + } + if (typeName.equals("char")) { + return Character.TYPE; + } + if (typeName.equals("float")) { + return Float.TYPE; + } + if (typeName.equals("double")) { + return Double.TYPE; + } + if (typeName.equals("void")) { + return Void.TYPE; + } + return null; + } + + + public T call(Object... params) { + T obj = null; + try { + obj = (T) method.invoke(null, params); + } catch (Exception e) { + e.printStackTrace(); + } + return obj; + } + + public T callWithException(Object... params) throws Throwable { + try { + return (T) this.method.invoke(null, params); + } catch (InvocationTargetException e) { + if (e.getCause() != null) { + throw e.getCause(); + } + throw e; + } + } +} diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticObject.java b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticObject.java new file mode 100644 index 0000000..7a4f9e4 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/java/mirror/RefStaticObject.java @@ -0,0 +1,35 @@ +package mirror; + +import java.lang.reflect.Field; + +@SuppressWarnings("unchecked") +public class RefStaticObject { + private Field field; + + public RefStaticObject(Class cls, Field field) throws NoSuchFieldException { + this.field = cls.getDeclaredField(field.getName()); + this.field.setAccessible(true); + } + + public Class type() { + return field.getType(); + } + + public T get() { + T obj = null; + try { + obj = (T) this.field.get(null); + } catch (Exception e) { + //Ignore + } + return obj; + } + + public void set(T obj) { + try { + this.field.set(null, obj); + } catch (Exception e) { + //Ignore + } + } +} \ No newline at end of file diff --git a/src/FakeLocation-master/FakeLocation-master/app/src/main/res/layout/activity_lucky_money_setting.xml b/src/FakeLocation-master/FakeLocation-master/app/src/main/res/layout/activity_lucky_money_setting.xml new file mode 100644 index 0000000..fdd8e19 --- /dev/null +++ b/src/FakeLocation-master/FakeLocation-master/app/src/main/res/layout/activity_lucky_money_setting.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/res/layout/reboot.xml b/src/XPrivacy-master/XPrivacy-master/res/layout/reboot.xml new file mode 100644 index 0000000..e8c069f --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/res/layout/reboot.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/res/layout/register.xml b/src/XPrivacy-master/XPrivacy-master/res/layout/register.xml new file mode 100644 index 0000000..d64560d --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/res/layout/register.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionchild.xml b/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionchild.xml new file mode 100644 index 0000000..b7e4b69 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionchild.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionentry.xml b/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionentry.xml new file mode 100644 index 0000000..5e6659b --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionentry.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionlist.xml b/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionlist.xml new file mode 100644 index 0000000..2c78751 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/res/layout/restrictionlist.xml @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Home + + + + + +
+connect_errno) { + log_error('web: database connect: ' . $db->connect_error, $my_email); + echo '
Error connecting to database
'; + exit(); + } + + // Select character set + $db->query("SET NAMES 'utf8'"); +?> + + + + +
+'; + foreach (range('A', 'Z') as $alpha) { + echo '' . $alpha . ' '; + } + echo 'other '; + echo '

'; + } + else { +?> + +

Rows marked with a grey background will be restricted when fetched; + bold text means data was used

+real_escape_string($package_name) . "'"; + $sql .= " ORDER BY package_version"; + $result = $db->query($sql); + if ($result) { + echo '

'; + while (($row = $result->fetch_object())) { + echo ''; + echo $row->package_version . ' '; + if (version_compare($row->package_version, $last_version, '>')) + $last_version = $row->package_version; + } + echo '

'; + $result->close(); + } + + if (empty($package_version)) + $package_version = $last_version; + } +?> + + + + + + + + + + + + + + + + + + + + +real_escape_string($letter) . "%'"; + $sql .= " ORDER BY application_name, package_name"; + $result = $db->query($sql); + if ($result) { + while (($row = $result->fetch_object())) { + $count++; + $name = (empty($row->application_name) ? '---' : $row->application_name); + echo ''; + + echo ''; + + echo ''; + echo '' . PHP_EOL; + } + $result->close(); + } + else + log_error('web: query application list: ' . $db->error . ' query=' . $sql, $my_email); + } else { + // Display application details + $sql = "SELECT restriction, method"; + $sql .= ", SUM(CASE WHEN restricted = 1 THEN 1 ELSE 0 END) AS restricted"; + $sql .= ", SUM(CASE WHEN restricted != 1 THEN 1 ELSE 0 END) AS not_restricted"; + $sql .= ", SUM(CASE WHEN allowed > 0 THEN 1 ELSE 0 END) AS allowed"; + $sql .= ", SUM(CASE WHEN allowed <= 0 THEN 1 ELSE 0 END) AS not_allowed"; + $sql .= ", SUM(CASE WHEN restricted = 1 AND package_version = '" . $db->real_escape_string($package_version) . "' THEN 1 ELSE 0 END) AS restricted_package"; + $sql .= ", SUM(CASE WHEN restricted != 1 AND package_version = '" . $db->real_escape_string($package_version) . "' THEN 1 ELSE 0 END) AS not_restricted_package"; + $sql .= ", SUM(CASE WHEN allowed > 0 AND package_version = '" . $db->real_escape_string($package_version) . "' THEN 1 ELSE 0 END) AS allowed_package"; + $sql .= ", SUM(CASE WHEN allowed <= 0 AND package_version = '" . $db->real_escape_string($package_version) . "' THEN 1 ELSE 0 END) AS not_allowed_package"; + $sql .= ", MAX(used) AS used"; + $sql .= ", MAX(modified) AS modified"; + $sql .= ", SUM(updates) AS updates"; + $sql .= " FROM xprivacy"; + $sql .= " WHERE package_name = '" . $db->real_escape_string($package_name) . "'"; + $sql .= " GROUP BY restriction, method"; + $sql .= " ORDER BY restriction, method"; + $result = $db->query($sql); + if ($result) { + while (($row = $result->fetch_object())) { + $count++; + $votes += $row->restricted + $row->not_restricted; + + $ci = confidence($row->restricted, $row->not_restricted); + $diff = $row->restricted / ($row->restricted + $row->not_restricted); + $restrict = ($ci < $max_confidence && $diff > $min_diff && $row->allowed <= $row->not_allowed); + + echo 'method)) + echo ' class="details"'; + echo '>'; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + echo ''; + echo '' . PHP_EOL; + } + $result->close(); + } + else + log_error('web: query application details: ' . $db->error . ' query=' . $sql, $my_email); + } +?> + +
ApplicationPackageAll versions
deny/allow *
Version
deny/allow
Exceptions
(yes/no)
CI95 ±% **Restriction
'; + echo htmlentities($name, ENT_COMPAT, 'UTF-8') . '' . htmlentities($row->package_name, ENT_COMPAT, 'UTF-8') . '
'; + echo ($row->restricted < $row->not_restricted) ? '' . $row->restricted . '' : $row->restricted; + echo ' / '; + echo ($row->restricted > $row->not_restricted) ? '' . $row->not_restricted . '' : $row->not_restricted; + echo ' ' . number_format($diff * 100, 0) . '%'; + echo ''; + echo $row->restricted_package . ' / ' . $row->not_restricted_package; + echo ''; + echo ($row->allowed . ' / ' . $row->not_allowed); + echo ''; + echo number_format($ci * 100, 1); + echo '' . ($row->method ? '' : + '' . + htmlentities($row->restriction, ENT_COMPAT, 'UTF-8') . '') . '
+ +

+ * More than % of the votes is required
+ ** Calculated using a + Agresti-Coull interval of 95%; + values below % are considered reliable.
+

+ +
+ +
+

+ +

+
+ +
+ Privacy policy + +

Copyright © 2013– by Marcel Bokhorst

+
+close(); +?> +
+ + + + + + diff --git a/src/XPrivacy-master/XPrivacy-master/server/xprivacy.sql b/src/XPrivacy-master/XPrivacy-master/server/xprivacy.sql new file mode 100644 index 0000000..7efef39 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/server/xprivacy.sql @@ -0,0 +1,119 @@ +-- phpMyAdmin SQL Dump +-- version 4.2.0 +-- http://www.phpmyadmin.net +-- +-- Host: localhost +-- Generation Time: Aug 18, 2014 at 04:19 PM +-- Server version: 5.6.19-1~dotdeb.1-log +-- PHP Version: 5.5.15-1~dotdeb.1 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + +-- +-- Database: `updates.faircode.eu` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `xprivacy` +-- + +CREATE TABLE IF NOT EXISTS `xprivacy` ( +`id` int(11) NOT NULL, + `android_id_md5` text NOT NULL, + `android_sdk` int(11) NOT NULL, + `xprivacy_version` int(11) DEFAULT NULL, + `package_name` text NOT NULL, + `package_version` text NOT NULL, + `package_version_code` int(11) NOT NULL, + `restriction` text NOT NULL, + `method` text NOT NULL, + `restricted` bit(1) NOT NULL, + `allowed` int(11) NOT NULL DEFAULT '0', + `used` bigint(13) NOT NULL, + `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updates` int(11) NOT NULL DEFAULT '1' +) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=8302378 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `xprivacy_app` +-- + +CREATE TABLE IF NOT EXISTS `xprivacy_app` ( +`id` int(11) NOT NULL, + `application_name` text CHARACTER SET utf8 NOT NULL, + `package_name` text CHARACTER SET utf8 NOT NULL, + `package_version` text CHARACTER SET utf8 NOT NULL, + `package_version_code` int(11) NOT NULL, + `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=56302 ; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `xprivacy_update` +-- + +CREATE TABLE IF NOT EXISTS `xprivacy_update` ( +`id` int(11) NOT NULL, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `android_id_md5` text CHARACTER SET utf8, + `installed_version` text CHARACTER SET utf8 NOT NULL, + `test_versions` int(11) NOT NULL, + `current_version` text CHARACTER SET utf8 NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1705 ; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `xprivacy` +-- +ALTER TABLE `xprivacy` + ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `restriction` (`android_id_md5`(50),`android_sdk`,`package_name`(100),`package_version`(50),`package_version_code`,`restriction`(20),`method`(70)), ADD KEY `package` (`package_name`(100)); + +-- +-- Indexes for table `xprivacy_app` +-- +ALTER TABLE `xprivacy_app` + ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `application` (`application_name`(100),`package_name`(100),`package_version`(50),`package_version_code`); + +-- +-- Indexes for table `xprivacy_update` +-- +ALTER TABLE `xprivacy_update` + ADD PRIMARY KEY (`id`), ADD KEY `android_id_time` (`android_id_md5`(50),`time`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `xprivacy` +-- +ALTER TABLE `xprivacy` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=8302378; +-- +-- AUTO_INCREMENT for table `xprivacy_app` +-- +ALTER TABLE `xprivacy_app` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=56302; +-- +-- AUTO_INCREMENT for table `xprivacy_update` +-- +ALTER TABLE `xprivacy_update` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=1705; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityApp.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityApp.java new file mode 100644 index 0000000..16f48dc --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityApp.java @@ -0,0 +1,1712 @@ +package biz.bokhorst.xprivacy; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.annotation.SuppressLint; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.graphics.Color; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.os.Process; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds.GroupMembership; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.support.v4.app.NavUtils; +import android.support.v4.app.TaskStackBuilder; +import android.support.v7.widget.SwitchCompat; +import android.support.v7.widget.Toolbar; +import android.text.Html; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.text.method.LinkMovementMethod; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.Window; +import android.widget.BaseExpandableListAdapter; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.OnGroupExpandListener; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +public class ActivityApp extends ActivityBase { + private ApplicationInfoEx mAppInfo = null; + private SwitchCompat swEnabled = null; + private RestrictionAdapter mPrivacyListAdapter = null; + + public static final String cUid = "Uid"; + public static final String cRestrictionName = "RestrictionName"; + public static final String cMethodName = "MethodName"; + public static final String cAction = "Action"; + public static final int cActionClear = 1; + public static final int cActionSettings = 2; + public static final int cActionRefresh = 3; + + private static final int MENU_LAUNCH = 1; + private static final int MENU_SETTINGS = 2; + private static final int MENU_KILL = 3; + private static final int MENU_STORE = 4; + + private static ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), + new PriorityThreadFactory()); + + private static class PriorityThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.NORM_PRIORITY); + return t; + } + } + + private boolean mPackageChangeReceiverRegistered = false; + + private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + if (mAppInfo.getUid() == uid) + finish(); + } + }; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final int userId = Util.getUserId(Process.myUid()); + + // Check privacy service client + if (!PrivacyService.checkClient()) + return; + + // Set layout + setContentView(R.layout.restrictionlist); + setSupportActionBar((Toolbar) findViewById(R.id.widgetToolbar)); + + // Annotate + Meta.annotate(this.getResources()); + + // Get arguments + Bundle extras = getIntent().getExtras(); + if (extras == null) { + finish(); + return; + } + + int uid = extras.getInt(cUid); + String restrictionName = (extras.containsKey(cRestrictionName) ? extras.getString(cRestrictionName) : null); + String methodName = (extras.containsKey(cMethodName) ? extras.getString(cMethodName) : null); + + // Get app info + mAppInfo = new ApplicationInfoEx(this, uid); + if (mAppInfo.getPackageName().size() == 0) { + finish(); + return; + } + + // Set sub title + getSupportActionBar().setSubtitle(TextUtils.join(", ", mAppInfo.getApplicationName())); + + // Handle info click + ImageView imgInfo = (ImageView) findViewById(R.id.imgInfo); + imgInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Packages can be selected on the web site + Util.viewUri(ActivityApp.this, Uri.parse(String.format(ActivityShare.getBaseURL() + "?package_name=%s", + mAppInfo.getPackageName().get(0)))); + } + }); + + // Display app name + TextView tvAppName = (TextView) findViewById(R.id.tvApp); + tvAppName.setText(mAppInfo.toString()); + + // Background color + if (mAppInfo.isSystem()) { + LinearLayout llInfo = (LinearLayout) findViewById(R.id.llInfo); + llInfo.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_dangerous))); + } + + // Display app icon + final ImageView imgIcon = (ImageView) findViewById(R.id.imgIcon); + imgIcon.setImageDrawable(mAppInfo.getIcon(this)); + + // Handle icon click + imgIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openContextMenu(imgIcon); + } + }); + + // Display on-demand state + final ImageView imgCbOnDemand = (ImageView) findViewById(R.id.imgCbOnDemand); + boolean isApp = PrivacyManager.isApplication(mAppInfo.getUid()); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, false); + boolean gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + if ((isApp || odSystem) && gondemand) { + boolean ondemand = PrivacyManager + .getSettingBool(-mAppInfo.getUid(), PrivacyManager.cSettingOnDemand, false); + imgCbOnDemand.setImageBitmap(ondemand ? getOnDemandCheckBox() : getOffCheckBox()); + + imgCbOnDemand.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + boolean ondemand = !PrivacyManager.getSettingBool(-mAppInfo.getUid(), + PrivacyManager.cSettingOnDemand, false); + PrivacyManager.setSetting(mAppInfo.getUid(), PrivacyManager.cSettingOnDemand, + Boolean.toString(ondemand)); + imgCbOnDemand.setImageBitmap(ondemand ? getOnDemandCheckBox() : getOffCheckBox()); + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } + }); + } else + imgCbOnDemand.setVisibility(View.GONE); + + // Display restriction state + swEnabled = (SwitchCompat) findViewById(R.id.swEnable); + swEnabled.setChecked(PrivacyManager.getSettingBool(mAppInfo.getUid(), PrivacyManager.cSettingRestricted, true)); + swEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PrivacyManager.setSetting(mAppInfo.getUid(), PrivacyManager.cSettingRestricted, + Boolean.toString(isChecked)); + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + imgCbOnDemand.setEnabled(isChecked); + } + }); + imgCbOnDemand.setEnabled(swEnabled.isChecked()); + + // Add context menu to icon + registerForContextMenu(imgIcon); + + // Check if internet access + if (!mAppInfo.hasInternet(this)) { + ImageView imgInternet = (ImageView) findViewById(R.id.imgInternet); + imgInternet.setVisibility(View.INVISIBLE); + } + + // Check if frozen + if (!mAppInfo.isFrozen(this)) { + ImageView imgFrozen = (ImageView) findViewById(R.id.imgFrozen); + imgFrozen.setVisibility(View.INVISIBLE); + } + + // Display version + TextView tvVersion = (TextView) findViewById(R.id.tvVersion); + tvVersion.setText(TextUtils.join(", ", mAppInfo.getPackageVersionName(this))); + + // Display package name + TextView tvPackageName = (TextView) findViewById(R.id.tvPackageName); + tvPackageName.setText(TextUtils.join(", ", mAppInfo.getPackageName())); + + // Fill privacy expandable list view adapter + final ExpandableListView elvRestriction = (ExpandableListView) findViewById(R.id.elvRestriction); + elvRestriction.setGroupIndicator(null); + mPrivacyListAdapter = new RestrictionAdapter(this, R.layout.restrictionentry, mAppInfo, restrictionName, + methodName); + elvRestriction.setAdapter(mPrivacyListAdapter); + + // Listen for group expand + elvRestriction.setOnGroupExpandListener(new OnGroupExpandListener() { + @Override + public void onGroupExpand(final int groupPosition) { + if (!PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingMethodExpert, false)) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityApp.this); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setMessage(R.string.msg_method_expert); + alertDialogBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + PrivacyManager.setSetting(userId, PrivacyManager.cSettingMethodExpert, + Boolean.toString(true)); + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } + }); + alertDialogBuilder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + elvRestriction.collapseGroup(groupPosition); + } + }); + alertDialogBuilder.setCancelable(false); + + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + + } + } + }); + + // Go to method + if (restrictionName != null) { + int groupPosition = new ArrayList(PrivacyManager.getRestrictions(this).values()) + .indexOf(restrictionName); + elvRestriction.setSelectedGroup(groupPosition); + elvRestriction.expandGroup(groupPosition); + if (methodName != null) { + Version version = new Version(Util.getSelfVersionName(this)); + int childPosition = PrivacyManager.getHooks(restrictionName, version).indexOf( + new Hook(restrictionName, methodName)); + elvRestriction.setSelectedChild(groupPosition, childPosition, true); + } + } + + // Listen for package add/remove + IntentFilter iff = new IntentFilter(); + iff.addAction(Intent.ACTION_PACKAGE_REMOVED); + iff.addDataScheme("package"); + registerReceiver(mPackageChangeReceiver, iff); + mPackageChangeReceiverRegistered = true; + + // Up navigation + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + // Tutorial + if (!PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingTutorialDetails, false)) { + ((ScrollView) findViewById(R.id.svTutorialHeader)).setVisibility(View.VISIBLE); + ((ScrollView) findViewById(R.id.svTutorialDetails)).setVisibility(View.VISIBLE); + } + View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + ViewParent parent = view.getParent(); + while (!parent.getClass().equals(ScrollView.class)) + parent = parent.getParent(); + ((View) parent).setVisibility(View.GONE); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingTutorialDetails, Boolean.TRUE.toString()); + } + }; + ((Button) findViewById(R.id.btnTutorialHeader)).setOnClickListener(listener); + ((Button) findViewById(R.id.btnTutorialDetails)).setOnClickListener(listener); + + // Process actions + if (extras.containsKey(cAction)) { + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(mAppInfo.getUid()); + if (extras.getInt(cAction) == cActionClear) + optionClear(); + else if (extras.getInt(cAction) == cActionSettings) + optionSettings(); + } + } + + @Override + protected void onNewIntent(Intent intent) { + Bundle extras = intent.getExtras(); + if (extras != null && extras.containsKey(cAction) && extras.getInt(cAction) == cActionRefresh) { + // Update on demand check box + ImageView imgCbOnDemand = (ImageView) findViewById(R.id.imgCbOnDemand); + boolean ondemand = PrivacyManager + .getSettingBool(-mAppInfo.getUid(), PrivacyManager.cSettingOnDemand, false); + imgCbOnDemand.setImageBitmap(ondemand ? getOnDemandCheckBox() : getOffCheckBox()); + + // Update restriction list + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } else { + setIntent(intent); + recreate(); + } + } + + @Override + protected void onResume() { + super.onResume(); + + // Update switch + if (swEnabled != null) + swEnabled.setChecked(PrivacyManager.getSettingBool(mAppInfo.getUid(), PrivacyManager.cSettingRestricted, + true)); + + // Update on demand check box + int userId = Util.getUserId(Process.myUid()); + ImageView imgCbOnDemand = (ImageView) findViewById(R.id.imgCbOnDemand); + boolean isApp = PrivacyManager.isApplication(mAppInfo.getUid()); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, false); + boolean gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + if ((isApp || odSystem) && gondemand) { + boolean ondemand = PrivacyManager + .getSettingBool(-mAppInfo.getUid(), PrivacyManager.cSettingOnDemand, false); + imgCbOnDemand.setImageBitmap(ondemand ? getOnDemandCheckBox() : getOffCheckBox()); + } + + // Update list + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mPackageChangeReceiverRegistered) { + unregisterReceiver(mPackageChangeReceiver); + mPackageChangeReceiverRegistered = false; + } + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + MenuInflater inflater = getMenuInflater(); + if (inflater != null && PrivacyService.checkClient()) { + inflater.inflate(R.menu.app, menu); + + // Add all contact groups + menu.findItem(R.id.menu_contacts).getSubMenu() + .add(-1, R.id.menu_contacts, Menu.NONE, getString(R.string.menu_all)); + + // Add other contact groups in the background + new AsyncTask() { + @Override + protected Object doInBackground(Object... arg0) { + try { + String where = ContactsContract.Groups.GROUP_VISIBLE + " = 1"; + where += " AND " + ContactsContract.Groups.SUMMARY_COUNT + " > 0"; + Cursor cursor = getContentResolver().query( + ContactsContract.Groups.CONTENT_SUMMARY_URI, + new String[] { ContactsContract.Groups._ID, ContactsContract.Groups.TITLE, + ContactsContract.Groups.ACCOUNT_NAME, ContactsContract.Groups.SUMMARY_COUNT }, + where, null, + ContactsContract.Groups.TITLE + ", " + ContactsContract.Groups.ACCOUNT_NAME); + + if (cursor != null) + try { + while (cursor.moveToNext()) { + int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.Groups._ID)); + String title = cursor.getString(cursor + .getColumnIndex(ContactsContract.Groups.TITLE)); + String account = cursor.getString(cursor + .getColumnIndex(ContactsContract.Groups.ACCOUNT_NAME)); + menu.findItem(R.id.menu_contacts).getSubMenu() + .add(id, R.id.menu_contacts, Menu.NONE, title + "/" + account); + } + } finally { + cursor.close(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + + return null; + } + }.executeOnExecutor(mExecutor); + + return true; + } else + return false; + } + + // Application context menu + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + + // Check if running + boolean running = false; + ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); + for (RunningAppProcessInfo info : activityManager.getRunningAppProcesses()) + if (info.uid == mAppInfo.getUid()) + running = true; + + PackageManager pm = getPackageManager(); + List listPackageNames = mAppInfo.getPackageName(); + List listApplicationName = mAppInfo.getApplicationName(); + for (int i = 0; i < listPackageNames.size(); i++) { + Menu appMenu = (listPackageNames.size() == 1) ? menu : menu.addSubMenu(i, Menu.NONE, Menu.NONE, + listApplicationName.get(i)); + + // Launch + MenuItem launch = appMenu.add(i, MENU_LAUNCH, Menu.NONE, getString(R.string.menu_app_launch)); + if (pm.getLaunchIntentForPackage(listPackageNames.get(i)) == null) + launch.setEnabled(false); + + // Settings + appMenu.add(i, MENU_SETTINGS, Menu.NONE, getString(R.string.menu_app_settings)); + + // Kill + MenuItem kill = appMenu.add(i, MENU_KILL, Menu.NONE, getString(R.string.menu_app_kill)); + kill.setEnabled(running && PrivacyManager.isApplication(mAppInfo.getUid())); + + // Play store + MenuItem store = appMenu.add(i, MENU_STORE, Menu.NONE, getString(R.string.menu_app_store)); + if (!Util.hasMarketLink(this, listPackageNames.get(i))) + store.setEnabled(false); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_LAUNCH: + optionLaunch(item.getGroupId()); + return true; + case MENU_SETTINGS: + optionAppSettings(item.getGroupId()); + return true; + case MENU_KILL: + optionKill(item.getGroupId()); + return true; + case MENU_STORE: + optionStore(item.getGroupId()); + return true; + default: + return super.onContextItemSelected(item); + } + } + + private void optionLaunch(int which) { + Intent intentLaunch = getPackageManager().getLaunchIntentForPackage(mAppInfo.getPackageName().get(which)); + startActivity(intentLaunch); + } + + private void optionAppSettings(int which) { + Intent intentSettings = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.parse("package:" + mAppInfo.getPackageName().get(which))); + startActivity(intentSettings); + } + + private void optionKill(final int which) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityApp.this); + alertDialogBuilder.setTitle(R.string.menu_app_kill); + alertDialogBuilder.setMessage(R.string.msg_sure); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int _which) { + XApplication.manage(ActivityApp.this, mAppInfo.getPackageName().get(which), + XApplication.cActionKillProcess); + Toast.makeText(ActivityApp.this, getString(R.string.msg_done), Toast.LENGTH_LONG).show(); + } + }); + alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + private void optionStore(int which) { + Util.viewUri(this, Uri.parse("market://details?id=" + mAppInfo.getPackageName().get(which))); + } + + // Options + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + boolean accountsRestricted = PrivacyManager.getRestrictionEx(mAppInfo.getUid(), PrivacyManager.cAccounts, null).restricted; + boolean appsRestricted = PrivacyManager.getRestrictionEx(mAppInfo.getUid(), PrivacyManager.cSystem, null).restricted; + boolean contactsRestricted = PrivacyManager.getRestrictionEx(mAppInfo.getUid(), PrivacyManager.cContacts, null).restricted; + + menu.findItem(R.id.menu_accounts).setEnabled(accountsRestricted); + menu.findItem(R.id.menu_applications).setEnabled(appsRestricted); + menu.findItem(R.id.menu_contacts).setEnabled(contactsRestricted); + + boolean mounted = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + menu.findItem(R.id.menu_export).setEnabled(mounted); + menu.findItem(R.id.menu_import).setEnabled(mounted); + + menu.findItem(R.id.menu_submit).setEnabled(Util.hasValidFingerPrint(this)); + + menu.findItem(R.id.menu_dump).setVisible(Util.isDebuggable(this)); + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + Intent upIntent = NavUtils.getParentActivityIntent(this); + if (upIntent != null) + if (NavUtils.shouldUpRecreateTask(this, upIntent)) + TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities(); + else + NavUtils.navigateUpTo(this, upIntent); + return true; + case R.id.menu_usage: + optionUsage(); + return true; + case R.id.menu_accounts: + optionAccounts(); + return true; + case R.id.menu_applications: + optionApplications(); + return true; + case R.id.menu_contacts: + if (item.getGroupId() != 0) { + optionContacts(item.getGroupId()); + return true; + } else + return false; + case R.id.menu_whitelists: + optionWhitelists(null); + return true; + case R.id.menu_apply: + optionTemplate(); + return true; + case R.id.menu_clear: + optionClear(); + return true; + case R.id.menu_export: + optionExport(); + return true; + case R.id.menu_import: + optionImport(); + return true; + case R.id.menu_submit: + optionSubmit(); + return true; + case R.id.menu_fetch: + optionFetch(); + return true; + case R.id.menu_settings: + optionSettings(); + return true; + case R.id.menu_dump: + optionDump(); + return true; + case R.id.menu_legend: + optionLegend(); + return true; + case R.id.menu_tutorial: + optionTutorial(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void optionUsage() { + Intent intent = new Intent(this, ActivityUsage.class); + intent.putExtra(ActivityUsage.cUid, mAppInfo.getUid()); + startActivity(intent); + } + + private void optionAccounts() { + AccountsTask accountsTask = new AccountsTask(); + accountsTask.executeOnExecutor(mExecutor, (Object) null); + } + + private void optionApplications() { + if (Util.hasProLicense(this) == null) { + // Redirect to pro page + Util.viewUri(this, ActivityMain.cProUri); + } else { + ApplicationsTask appsTask = new ApplicationsTask(); + appsTask.executeOnExecutor(mExecutor, (Object) null); + } + } + + private void optionContacts(int groupId) { + if (Util.hasProLicense(this) == null) { + // Redirect to pro page + Util.viewUri(this, ActivityMain.cProUri); + } else { + ContactsTask contactsTask = new ContactsTask(); + contactsTask.executeOnExecutor(mExecutor, groupId); + } + } + + private void optionWhitelists(String type) { + if (Util.hasProLicense(this) == null) { + // Redirect to pro page + Util.viewUri(this, ActivityMain.cProUri); + } else { + WhitelistTask whitelistsTask = new WhitelistTask(mAppInfo.getUid(), type, this); + whitelistsTask.executeOnExecutor(mExecutor, (Object) null); + } + } + + private void optionTemplate() { + Intent intent = new Intent(ActivityShare.ACTION_TOGGLE); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, new int[] { mAppInfo.getUid() }); + intent.putExtra(ActivityShare.cChoice, ActivityShare.CHOICE_TEMPLATE); + startActivity(intent); + } + + private void optionClear() { + Intent intent = new Intent(ActivityShare.ACTION_TOGGLE); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, new int[] { mAppInfo.getUid() }); + intent.putExtra(ActivityShare.cChoice, ActivityShare.CHOICE_CLEAR); + startActivity(intent); + } + + private void optionExport() { + Intent intent = new Intent(ActivityShare.ACTION_EXPORT); + intent.putExtra(ActivityShare.cUidList, new int[] { mAppInfo.getUid() }); + intent.putExtra(ActivityShare.cInteractive, true); + startActivity(intent); + } + + private void optionImport() { + Intent intent = new Intent(ActivityShare.ACTION_IMPORT); + intent.putExtra(ActivityShare.cUidList, new int[] { mAppInfo.getUid() }); + intent.putExtra(ActivityShare.cInteractive, true); + startActivity(intent); + } + + private void optionSubmit() { + if (ActivityShare.registerDevice(this)) { + Intent intent = new Intent("biz.bokhorst.xprivacy.action.SUBMIT"); + intent.putExtra(ActivityShare.cUidList, new int[] { mAppInfo.getUid() }); + intent.putExtra(ActivityShare.cInteractive, true); + startActivity(intent); + } + } + + private void optionFetch() { + Intent intent = new Intent("biz.bokhorst.xprivacy.action.FETCH"); + intent.putExtra(ActivityShare.cUidList, new int[] { mAppInfo.getUid() }); + intent.putExtra(ActivityShare.cInteractive, true); + startActivity(intent); + } + + private void optionSettings() { + Intent intent = new Intent(ActivityApp.this, ActivitySettings.class); + intent.putExtra(ActivitySettings.cUid, mAppInfo.getUid()); + startActivity(intent); + } + + private void optionDump() { + try { + PrivacyService.getClient().dump(mAppInfo.getUid()); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + private void optionLegend() { + // Show help + Dialog dialog = new Dialog(ActivityApp.this); + dialog.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dialog.setTitle(R.string.menu_legend); + dialog.setContentView(R.layout.legend); + dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, getThemed(R.attr.icon_launcher)); + + ((ImageView) dialog.findViewById(R.id.imgHelpHalf)).setImageBitmap(getHalfCheckBox()); + ((ImageView) dialog.findViewById(R.id.imgHelpOnDemand)).setImageBitmap(getOnDemandCheckBox()); + + for (View child : Util.getViewsByTag((ViewGroup) dialog.findViewById(android.R.id.content), "main")) + child.setVisibility(View.GONE); + + ((LinearLayout) dialog.findViewById(R.id.llUnsafe)).setVisibility(PrivacyManager.cVersion3 ? View.VISIBLE + : View.GONE); + + dialog.setCancelable(true); + dialog.show(); + } + + private void optionTutorial() { + ((ScrollView) findViewById(R.id.svTutorialHeader)).setVisibility(View.VISIBLE); + ((ScrollView) findViewById(R.id.svTutorialDetails)).setVisibility(View.VISIBLE); + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingTutorialDetails, Boolean.FALSE.toString()); + } + + // Tasks + + private class AccountsTask extends AsyncTask { + private List mListAccount; + private Account[] mAccounts; + private boolean[] mSelection; + + @Override + protected Object doInBackground(Object... params) { + // Get accounts + mListAccount = new ArrayList(); + AccountManager accountManager = AccountManager.get(ActivityApp.this); + mAccounts = accountManager.getAccounts(); + mSelection = new boolean[mAccounts.length]; + for (int i = 0; i < mAccounts.length; i++) + try { + mListAccount.add(String.format("%s (%s)", mAccounts[i].name, mAccounts[i].type)); + String sha1 = Util.sha1(mAccounts[i].name + mAccounts[i].type); + mSelection[i] = PrivacyManager.getSettingBool(-mAppInfo.getUid(), Meta.cTypeAccount, sha1, false); + } catch (Throwable ex) { + Util.bug(null, ex); + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (!ActivityApp.this.isFinishing()) { + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityApp.this); + alertDialogBuilder.setTitle(R.string.menu_accounts); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setMultiChoiceItems(mListAccount.toArray(new CharSequence[0]), mSelection, + new DialogInterface.OnMultiChoiceClickListener() { + public void onClick(DialogInterface dialog, int whichButton, boolean isChecked) { + try { + Account account = mAccounts[whichButton]; + String sha1 = Util.sha1(account.name + account.type); + PrivacyManager.setSetting(mAppInfo.getUid(), Meta.cTypeAccount, sha1, + Boolean.toString(isChecked)); + } catch (Throwable ex) { + Util.bug(null, ex); + Toast toast = Toast.makeText(ActivityApp.this, ex.toString(), Toast.LENGTH_LONG); + toast.show(); + } + } + }); + alertDialogBuilder.setPositiveButton(getString(R.string.msg_done), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + super.onPostExecute(result); + } + } + + private class ApplicationsTask extends AsyncTask { + private CharSequence[] mApp; + private String[] mPackage; + private boolean[] mSelection; + + @Override + protected Object doInBackground(Object... params) { + // Get applications + int count = 0; + Map> mapApp = new HashMap>(); + for (ApplicationInfoEx appInfo : ApplicationInfoEx.getXApplicationList(ActivityApp.this, null)) + for (int p = 0; p < appInfo.getPackageName().size(); p++) { + String appName = appInfo.getApplicationName().get(p); + List listPkg; + if (mapApp.containsKey(appName)) + listPkg = mapApp.get(appName); + else { + listPkg = new ArrayList(); + mapApp.put(appName, listPkg); + } + listPkg.add(appInfo.getPackageName().get(p)); + count++; + } + + // Sort applications + List listApp = new ArrayList(mapApp.keySet()); + Collator collator = Collator.getInstance(Locale.getDefault()); + Collections.sort(listApp, collator); + + // Build selection arrays + int i = 0; + mApp = new CharSequence[count]; + mPackage = new String[count]; + mSelection = new boolean[count]; + for (String appName : listApp) { + List listPkg = mapApp.get(appName); + Collections.sort(listPkg, collator); + for (String pkgName : listPkg) { + mApp[i] = (pkgName.equals(appName) ? appName : String.format("%s (%s)", appName, pkgName)); + mPackage[i] = pkgName; + mSelection[i] = PrivacyManager.getSettingBool(-mAppInfo.getUid(), Meta.cTypeApplication, pkgName, + false); + i++; + } + } + + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (!ActivityApp.this.isFinishing()) { + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityApp.this); + alertDialogBuilder.setTitle(R.string.menu_applications); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setMultiChoiceItems(mApp, mSelection, + new DialogInterface.OnMultiChoiceClickListener() { + public void onClick(DialogInterface dialog, int whichButton, boolean isChecked) { + try { + PrivacyManager.setSetting(mAppInfo.getUid(), Meta.cTypeApplication, + mPackage[whichButton], Boolean.toString(isChecked)); + } catch (Throwable ex) { + Util.bug(null, ex); + Toast toast = Toast.makeText(ActivityApp.this, ex.toString(), Toast.LENGTH_LONG); + toast.show(); + } + } + }); + alertDialogBuilder.setPositiveButton(getString(R.string.msg_done), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + super.onPostExecute(result); + } + } + + private class ContactsTask extends AsyncTask { + private List mListContact; + private long[] mIds; + private boolean[] mSelection; + + @Override + protected Object doInBackground(Integer... params) { + // Map contacts + Map mapContact = new LinkedHashMap(); + Cursor cursor; + if (params[0] < 0) + cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + new String[] { ContactsContract.Contacts._ID, Phone.DISPLAY_NAME }, null, null, + Phone.DISPLAY_NAME); + else + cursor = getContentResolver() + .query(ContactsContract.Data.CONTENT_URI, + new String[] { ContactsContract.Contacts._ID, Phone.DISPLAY_NAME, + GroupMembership.GROUP_ROW_ID }, GroupMembership.GROUP_ROW_ID + "= ?", + new String[] { Integer.toString(params[0]) }, Phone.DISPLAY_NAME); + if (cursor != null) + try { + while (cursor.moveToNext()) { + long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Contacts._ID)); + String contact = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); + if (contact != null) + mapContact.put(id, contact); + } + } finally { + cursor.close(); + } + + // Build dialog data + mListContact = new ArrayList(); + mIds = new long[mapContact.size() + 1]; + mSelection = new boolean[mapContact.size() + 1]; + + mListContact.add("[" + getString(R.string.menu_all) + "]"); + mIds[0] = -1; + mSelection[0] = false; + + int i = 0; + for (Long id : mapContact.keySet()) { + mListContact.add(mapContact.get(id)); + mIds[i + 1] = id; + mSelection[i++ + 1] = PrivacyManager.getSettingBool(-mAppInfo.getUid(), Meta.cTypeContact, + Long.toString(id), false); + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (!ActivityApp.this.isFinishing()) { + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityApp.this); + alertDialogBuilder.setTitle(R.string.menu_contacts); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setMultiChoiceItems(mListContact.toArray(new CharSequence[0]), mSelection, + new DialogInterface.OnMultiChoiceClickListener() { + public void onClick(final DialogInterface dialog, int whichButton, final boolean isChecked) { + // Contact + if (whichButton == 0) { + ((AlertDialog) dialog).getListView().setEnabled(false); + ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + ((AlertDialog) dialog).setCancelable(false); + + new AsyncTask() { + @Override + protected Object doInBackground(Object... arg0) { + for (int i = 1; i < mSelection.length; i++) { + mSelection[i] = isChecked; + PrivacyManager.setSetting(mAppInfo.getUid(), Meta.cTypeContact, + Long.toString(mIds[i]), Boolean.toString(mSelection[i])); + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + for (int i = 1; i < mSelection.length; i++) + ((AlertDialog) dialog).getListView().setItemChecked(i, mSelection[i]); + + ((AlertDialog) dialog).getListView().setEnabled(true); + ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled( + true); + ((AlertDialog) dialog).setCancelable(true); + } + }.executeOnExecutor(mExecutor); + + } else + PrivacyManager.setSetting(mAppInfo.getUid(), Meta.cTypeContact, + Long.toString(mIds[whichButton]), Boolean.toString(isChecked)); + } + }); + alertDialogBuilder.setPositiveButton(getString(R.string.msg_done), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (mPrivacyListAdapter != null) + mPrivacyListAdapter.notifyDataSetChanged(); + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + super.onPostExecute(result); + } + } + + // Adapters + + private class RestrictionAdapter extends BaseExpandableListAdapter { + private Context mContext; + private ApplicationInfoEx mAppInfo; + private String mSelectedRestrictionName; + private String mSelectedMethodName; + private List mListRestriction; + private HashMap> mHook; + private Version mVersion; + private LayoutInflater mInflater; + + public RestrictionAdapter(Context context, int resource, ApplicationInfoEx appInfo, + String selectedRestrictionName, String selectedMethodName) { + mContext = context; + mAppInfo = appInfo; + mSelectedRestrictionName = selectedRestrictionName; + mSelectedMethodName = selectedMethodName; + mListRestriction = new ArrayList(); + mHook = new LinkedHashMap>(); + mVersion = new Version(Util.getSelfVersionName(context)); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + int userId = Util.getUserId(Process.myUid()); + boolean fUsed = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUsed, false); + boolean fPermission = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFPermission, false); + + for (String rRestrictionName : PrivacyManager.getRestrictions(mContext).values()) { + boolean isUsed = (PrivacyManager.getUsage(mAppInfo.getUid(), rRestrictionName, null) > 0); + boolean hasPermission = PrivacyManager.hasPermission(mContext, mAppInfo, rRestrictionName, mVersion); + if (mSelectedRestrictionName != null + || ((fUsed ? isUsed : true) && (fPermission ? isUsed || hasPermission : true))) + mListRestriction.add(rRestrictionName); + } + } + + @Override + public Object getGroup(int groupPosition) { + return mListRestriction.get(groupPosition); + } + + @Override + public int getGroupCount() { + return mListRestriction.size(); + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition * 1000; + } + + private class GroupViewHolder { + private View row; + private int position; + public ImageView imgIndicator; + public ImageView imgUsed; + public ImageView imgGranted; + public ImageView imgInfo; + public TextView tvName; + public ImageView imgWhitelist; + public ImageView imgCbRestricted; + public ProgressBar pbRunning; + public ImageView imgCbAsk; + public LinearLayout llName; + + public GroupViewHolder(View theRow, int thePosition) { + row = theRow; + position = thePosition; + imgIndicator = (ImageView) row.findViewById(R.id.imgIndicator); + imgUsed = (ImageView) row.findViewById(R.id.imgUsed); + imgGranted = (ImageView) row.findViewById(R.id.imgGranted); + imgInfo = (ImageView) row.findViewById(R.id.imgInfo); + tvName = (TextView) row.findViewById(R.id.tvName); + imgWhitelist = (ImageView) row.findViewById(R.id.imgWhitelist); + imgCbRestricted = (ImageView) row.findViewById(R.id.imgCbRestricted); + pbRunning = (ProgressBar) row.findViewById(R.id.pbRunning); + imgCbAsk = (ImageView) row.findViewById(R.id.imgCbAsk); + llName = (LinearLayout) row.findViewById(R.id.llName); + } + } + + private class GroupHolderTask extends AsyncTask { + private int position; + private GroupViewHolder holder; + private String restrictionName; + private boolean used; + private boolean permission; + private RState rstate; + private boolean ondemand; + private boolean whitelist; + private boolean enabled; + private boolean can; + private boolean methodExpert; + + public GroupHolderTask(int thePosition, GroupViewHolder theHolder, String theRestrictionName) { + position = thePosition; + holder = theHolder; + restrictionName = theRestrictionName; + } + + @Override + protected Object doInBackground(Object... params) { + if (restrictionName != null) { + // Get info + int userId = Util.getUserId(Process.myUid()); + used = (PrivacyManager.getUsage(mAppInfo.getUid(), restrictionName, null) != 0); + permission = PrivacyManager.hasPermission(mContext, mAppInfo, restrictionName, mVersion); + rstate = new RState(mAppInfo.getUid(), restrictionName, null, mVersion); + + boolean isApp = PrivacyManager.isApplication(mAppInfo.getUid()); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, + false); + boolean gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + ondemand = ((isApp || odSystem) && gondemand); + if (ondemand) + ondemand = PrivacyManager.getSettingBool(-mAppInfo.getUid(), PrivacyManager.cSettingOnDemand, + false); + + whitelist = false; + String wName = null; + if (PrivacyManager.cAccounts.equals(restrictionName)) + wName = Meta.cTypeAccount; + else if (PrivacyManager.cSystem.equals(restrictionName)) + wName = Meta.cTypeApplication; + else if (PrivacyManager.cContacts.equals(restrictionName)) + wName = Meta.cTypeContact; + if (wName != null) { + boolean blacklist = PrivacyManager.getSettingBool(-mAppInfo.getUid(), + PrivacyManager.cSettingBlacklist, false); + if (blacklist) + whitelist = true; + else + for (PSetting setting : PrivacyManager.getSettingList(mAppInfo.getUid(), wName)) + if (Boolean.parseBoolean(setting.value)) { + whitelist = true; + break; + } + } + if (!whitelist) + for (Hook hook : PrivacyManager.getHooks(restrictionName, mVersion)) + if (hook.whitelist() != null) + if (PrivacyManager.getSettingList(mAppInfo.getUid(), hook.whitelist()).size() > 0) { + whitelist = true; + break; + } + + enabled = PrivacyManager.getSettingBool(mAppInfo.getUid(), PrivacyManager.cSettingRestricted, true); + can = PrivacyManager.canRestrict(rstate.mUid, Process.myUid(), rstate.mRestrictionName, null, true); + methodExpert = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingMethodExpert, false); + + return holder; + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (holder.position == position && result != null) { + // Set data + holder.tvName.setTypeface(null, used ? Typeface.BOLD_ITALIC : Typeface.NORMAL); + holder.imgUsed.setVisibility(used ? View.VISIBLE : View.INVISIBLE); + holder.imgGranted.setVisibility(permission ? View.VISIBLE : View.INVISIBLE); + + // Show whitelist icon + holder.imgWhitelist.setVisibility(whitelist ? View.VISIBLE : View.GONE); + if (whitelist) + holder.imgWhitelist.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (PrivacyManager.cAccounts.equals(restrictionName)) + optionAccounts(); + else if (PrivacyManager.cSystem.equals(restrictionName)) + optionApplications(); + else if (PrivacyManager.cContacts.equals(restrictionName)) + optionContacts(-1); + } + }); + else + holder.imgWhitelist.setClickable(false); + + // Display restriction + holder.imgCbRestricted.setImageBitmap(getCheckBoxImage(rstate, methodExpert)); + holder.imgCbRestricted.setVisibility(View.VISIBLE); + if (ondemand) { + holder.imgCbAsk.setImageBitmap(getAskBoxImage(rstate, methodExpert)); + holder.imgCbAsk.setVisibility(View.VISIBLE); + } else + holder.imgCbAsk.setVisibility(View.GONE); + + // Check if can be restricted + holder.llName.setEnabled(enabled && can); + holder.tvName.setEnabled(enabled && can); + holder.imgCbAsk.setEnabled(enabled && can); + + // Listen for restriction changes + holder.llName.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + holder.llName.setEnabled(false); + holder.imgCbRestricted.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + private List oldState; + private List newState; + + @Override + protected Object doInBackground(Object... arg0) { + // Change restriction + oldState = PrivacyManager.getRestartStates(mAppInfo.getUid(), restrictionName); + rstate.toggleRestriction(); + newState = PrivacyManager.getRestartStates(mAppInfo.getUid(), restrictionName); + return null; + } + + @Override + protected void onPostExecute(Object result) { + // Refresh display + // Needed to update children + notifyDataSetChanged(); + + // Notify restart + if (!newState.equals(oldState)) + Toast.makeText(mContext, getString(R.string.msg_restart), Toast.LENGTH_LONG) + .show(); + + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbRestricted.setVisibility(View.VISIBLE); + holder.llName.setEnabled(true); + } + }.executeOnExecutor(mExecutor); + } + }); + + // Listen for ask changes + if (ondemand) + holder.imgCbAsk.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + holder.imgCbAsk.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + @Override + protected Object doInBackground(Object... arg0) { + rstate.toggleAsked(); + return null; + } + + @Override + protected void onPostExecute(Object result) { + // Needed to update children + notifyDataSetChanged(); + + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbAsk.setVisibility(View.VISIBLE); + } + }.executeOnExecutor(mExecutor); + } + }); + else + holder.imgCbAsk.setClickable(false); + } + } + } + + @Override + @SuppressLint("InflateParams") + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + GroupViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.restrictionentry, null); + holder = new GroupViewHolder(convertView, groupPosition); + convertView.setTag(holder); + } else { + holder = (GroupViewHolder) convertView.getTag(); + holder.position = groupPosition; + } + + // Get entry + final String restrictionName = (String) getGroup(groupPosition); + + // Indicator state + holder.imgIndicator.setImageResource(getThemed(isExpanded ? R.attr.icon_expander_maximized + : R.attr.icon_expander_minimized)); + + // Disable indicator for empty groups + if (getChildrenCount(groupPosition) == 0) + holder.imgIndicator.setVisibility(View.INVISIBLE); + else + holder.imgIndicator.setVisibility(View.VISIBLE); + + // Display if used + holder.tvName.setTypeface(null, Typeface.NORMAL); + holder.imgUsed.setVisibility(View.INVISIBLE); + + // Check if permission + holder.imgGranted.setVisibility(View.INVISIBLE); + + // Display localized name + TreeMap tmRestriction = PrivacyManager.getRestrictions(mContext); + int index = new ArrayList(tmRestriction.values()).indexOf(restrictionName); + final String title = (String) tmRestriction.navigableKeySet().toArray()[index]; + holder.tvName.setText(title); + holder.imgWhitelist.setVisibility(View.GONE); + + // Display restriction + holder.imgCbRestricted.setVisibility(View.INVISIBLE); + holder.imgCbAsk.setVisibility(View.INVISIBLE); + + // Async update + new GroupHolderTask(groupPosition, holder, restrictionName).executeOnExecutor(mExecutor, (Object) null); + + // Handle info + holder.imgInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + int stringId = getResources().getIdentifier("restrict_help_" + restrictionName, "string", + getPackageName()); + + // Build dialog + Dialog dlgHelp = new Dialog(mContext); + dlgHelp.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dlgHelp.setTitle(title); + dlgHelp.setContentView(R.layout.helpcat); + dlgHelp.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, + ActivityApp.this.getThemed(R.attr.icon_launcher)); + dlgHelp.setCancelable(true); + + // Set info + TextView tvInfo = (TextView) dlgHelp.findViewById(R.id.tvInfo); + tvInfo.setText(stringId); + + dlgHelp.show(); + } + }); + + return convertView; + } + + private List getHooks(int groupPosition) { + if (!mHook.containsKey(groupPosition)) { + int userId = Util.getUserId(Process.myUid()); + boolean fUsed = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUsed, false); + boolean fPermission = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFPermission, false); + List listMethod = new ArrayList(); + String restrictionName = mListRestriction.get(groupPosition); + for (Hook hook : PrivacyManager.getHooks((String) getGroup(groupPosition), mVersion)) { + // Filter + boolean isUsed = (PrivacyManager.getUsage(mAppInfo.getUid(), restrictionName, hook.getName()) > 0); + boolean hasPermission = PrivacyManager.hasPermission(mContext, mAppInfo, hook); + if (mSelectedMethodName != null + || ((fUsed ? isUsed : true) && (fPermission ? isUsed || hasPermission : true))) + listMethod.add(hook); + } + mHook.put(groupPosition, listMethod); + } + return mHook.get(groupPosition); + } + + @Override + public Object getChild(int groupPosition, int childPosition) { + return getHooks(groupPosition).get(childPosition); + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return groupPosition * 1000 + childPosition; + } + + @Override + public int getChildrenCount(int groupPosition) { + return getHooks(groupPosition).size(); + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return false; + } + + private class ChildViewHolder { + private View row; + private int groupPosition; + private int childPosition; + public ImageView imgUsed; + public ImageView imgGranted; + public ImageView imgInfo; + public TextView tvMethodName; + public ImageView imgUnsafe; + public ImageView imgMethodWhitelist; + public ImageView imgCbMethodRestricted; + public ProgressBar pbRunning; + public ImageView imgCbMethodAsk; + public LinearLayout llMethodName; + + private ChildViewHolder(View theRow, int gPosition, int cPosition) { + row = theRow; + groupPosition = gPosition; + childPosition = cPosition; + imgUsed = (ImageView) row.findViewById(R.id.imgUsed); + imgGranted = (ImageView) row.findViewById(R.id.imgGranted); + imgInfo = (ImageView) row.findViewById(R.id.imgInfo); + tvMethodName = (TextView) row.findViewById(R.id.tvMethodName); + imgUnsafe = (ImageView) row.findViewById(R.id.imgUnsafe); + imgMethodWhitelist = (ImageView) row.findViewById(R.id.imgMethodWhitelist); + imgCbMethodRestricted = (ImageView) row.findViewById(R.id.imgCbMethodRestricted); + pbRunning = (ProgressBar) row.findViewById(R.id.pbRunning); + imgCbMethodAsk = (ImageView) row.findViewById(R.id.imgCbMethodAsk); + llMethodName = (LinearLayout) row.findViewById(R.id.llMethodName); + } + } + + private class ChildHolderTask extends AsyncTask { + private int groupPosition; + private int childPosition; + private ChildViewHolder holder; + private String restrictionName; + private Hook md; + private long lastUsage; + private PRestriction parent; + private boolean permission; + private RState rstate; + private boolean ondemand; + private boolean whitelist; + private boolean enabled; + private boolean can; + + public ChildHolderTask(int gPosition, int cPosition, ChildViewHolder theHolder, String theRestrictionName) { + groupPosition = gPosition; + childPosition = cPosition; + holder = theHolder; + restrictionName = theRestrictionName; + } + + @Override + protected Object doInBackground(Object... params) { + if (restrictionName != null) { + // Get info + int userId = Util.getUserId(Process.myUid()); + md = (Hook) getChild(groupPosition, childPosition); + lastUsage = PrivacyManager.getUsage(mAppInfo.getUid(), restrictionName, md.getName()); + parent = PrivacyManager.getRestrictionEx(mAppInfo.getUid(), restrictionName, null); + permission = PrivacyManager.hasPermission(mContext, mAppInfo, md); + rstate = new RState(mAppInfo.getUid(), restrictionName, md.getName(), mVersion); + + boolean isApp = PrivacyManager.isApplication(mAppInfo.getUid()); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, + false); + boolean gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + + ondemand = ((isApp || odSystem) && gondemand); + if (ondemand) + ondemand = PrivacyManager.getSettingBool(-mAppInfo.getUid(), PrivacyManager.cSettingOnDemand, + false); + + if (md.whitelist() == null) + whitelist = false; + else + whitelist = PrivacyManager.listWhitelisted(mAppInfo.getUid(), md.whitelist()).size() > 0; + + enabled = PrivacyManager.getSettingBool(mAppInfo.getUid(), PrivacyManager.cSettingRestricted, true); + can = PrivacyManager.canRestrict(rstate.mUid, Process.myUid(), rstate.mRestrictionName, + rstate.mMethodName, true); + return holder; + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (holder.groupPosition == groupPosition && holder.childPosition == childPosition && result != null) { + // Set data + if (lastUsage > 0) { + CharSequence sLastUsage = DateUtils.getRelativeTimeSpanString(lastUsage, new Date().getTime(), + DateUtils.SECOND_IN_MILLIS, 0); + holder.tvMethodName.setText(String.format("%s (%s)", md.getName(), sLastUsage)); + } + holder.llMethodName.setEnabled(parent.restricted); + holder.tvMethodName.setEnabled(parent.restricted); + holder.imgCbMethodAsk.setEnabled(!parent.asked); + + holder.imgUsed.setImageResource(getThemed(md.hasUsageData() && enabled && can ? R.attr.icon_used + : R.attr.icon_used_grayed)); + holder.imgUsed.setVisibility(lastUsage == 0 && md.hasUsageData() && enabled && can ? View.INVISIBLE + : View.VISIBLE); + holder.tvMethodName.setTypeface(null, lastUsage == 0 ? Typeface.NORMAL : Typeface.BOLD_ITALIC); + holder.imgGranted.setVisibility(permission ? View.VISIBLE : View.INVISIBLE); + + // Show whitelist icon + holder.imgMethodWhitelist.setVisibility(whitelist ? View.VISIBLE : View.INVISIBLE); + if (whitelist) + holder.imgMethodWhitelist.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ActivityApp.this.optionWhitelists(md.whitelist()); + } + }); + else + holder.imgMethodWhitelist.setClickable(false); + + // Display restriction + holder.imgCbMethodRestricted.setImageBitmap(getCheckBoxImage(rstate, true)); + holder.imgCbMethodRestricted.setVisibility(View.VISIBLE); + + // Show asked state + if (ondemand) { + holder.imgCbMethodAsk.setImageBitmap(getAskBoxImage(rstate, true)); + holder.imgCbMethodAsk.setVisibility(md.canOnDemand() ? View.VISIBLE : View.INVISIBLE); + } else + holder.imgCbMethodAsk.setVisibility(View.GONE); + + holder.llMethodName.setEnabled(enabled && can); + holder.tvMethodName.setEnabled(enabled && can); + holder.imgCbMethodAsk.setEnabled(enabled && can); + + // Listen for restriction changes + if (parent.restricted) + holder.llMethodName.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + holder.llMethodName.setEnabled(false); + holder.imgCbMethodRestricted.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + @Override + protected Object doInBackground(Object... arg0) { + // Change restriction + rstate.toggleRestriction(); + return null; + } + + @Override + protected void onPostExecute(Object result) { + // Refresh display + // Needed to update parent + notifyDataSetChanged(); + + // Notify restart + if (md.isRestartRequired()) + Toast.makeText(mContext, getString(R.string.msg_restart), Toast.LENGTH_LONG) + .show(); + + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbMethodRestricted.setVisibility(View.VISIBLE); + holder.llMethodName.setEnabled(true); + } + }.executeOnExecutor(mExecutor); + } + }); + else + holder.llMethodName.setClickable(false); + + // Listen for ask changes + if (ondemand && !parent.asked) + holder.imgCbMethodAsk.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + holder.imgCbMethodAsk.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + @Override + protected Object doInBackground(Object... arg0) { + rstate.toggleAsked(); + return null; + } + + @Override + protected void onPostExecute(Object result) { + // Needed to update parent + notifyDataSetChanged(); + + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbMethodAsk.setVisibility(View.VISIBLE); + } + }.executeOnExecutor(mExecutor); + } + }); + else + holder.imgCbMethodAsk.setClickable(false); + } + } + } + + @Override + @SuppressLint("InflateParams") + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, + ViewGroup parent) { + ChildViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.restrictionchild, null); + holder = new ChildViewHolder(convertView, groupPosition, childPosition); + convertView.setTag(holder); + } else { + holder = (ChildViewHolder) convertView.getTag(); + holder.groupPosition = groupPosition; + holder.childPosition = childPosition; + } + + // Get entry + final String restrictionName = (String) getGroup(groupPosition); + final Hook hook = (Hook) getChild(groupPosition, childPosition); + + // Set background color + if (hook.isDangerous()) + holder.row.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_dangerous))); + else + holder.row.setBackgroundColor(Color.TRANSPARENT); + + holder.llMethodName.setEnabled(false); + holder.tvMethodName.setEnabled(false); + holder.imgCbMethodAsk.setEnabled(false); + + // Display method name + holder.tvMethodName.setText(hook.getName()); + holder.tvMethodName.setTypeface(null, Typeface.NORMAL); + + // Display if used + holder.imgUsed.setVisibility(View.INVISIBLE); + + // Hide if permissions + holder.imgGranted.setVisibility(View.INVISIBLE); + + // Function help + if (hook.getAnnotation() == null) + holder.imgInfo.setVisibility(View.GONE); + else { + holder.imgInfo.setVisibility(View.VISIBLE); + holder.imgInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + View parent = ActivityApp.this.findViewById(android.R.id.content); + showHelp(ActivityApp.this, parent, hook); + } + }); + } + + // Show if unsafe + holder.imgUnsafe.setVisibility(hook != null && hook.isUnsafe() ? View.VISIBLE : View.INVISIBLE); + + // Hide whitelist icon + holder.imgMethodWhitelist.setVisibility(View.INVISIBLE); + + // Display restriction + holder.llMethodName.setClickable(false); + holder.imgCbMethodRestricted.setVisibility(View.INVISIBLE); + holder.imgCbMethodAsk.setVisibility(View.INVISIBLE); + + // Async update + new ChildHolderTask(groupPosition, childPosition, holder, restrictionName).executeOnExecutor(mExecutor, + (Object) null); + + return convertView; + } + + @Override + public boolean hasStableIds() { + return true; + } + } + + @SuppressLint("InflateParams") + public static void showHelp(ActivityBase context, View parent, Hook hook) { + // Build dialog + Dialog dlgHelp = new Dialog(context); + dlgHelp.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dlgHelp.setTitle(R.string.app_name); + dlgHelp.setContentView(R.layout.helpfunc); + dlgHelp.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, context.getThemed(R.attr.icon_launcher)); + dlgHelp.setCancelable(true); + + // Set title + TextView tvTitle = (TextView) dlgHelp.findViewById(R.id.tvTitle); + tvTitle.setText(hook.getName()); + + // Set info + TextView tvInfo = (TextView) dlgHelp.findViewById(R.id.tvInfo); + tvInfo.setText(Html.fromHtml(hook.getAnnotation())); + tvInfo.setMovementMethod(LinkMovementMethod.getInstance()); + + // Set permissions + String[] permissions = hook.getPermissions(); + if (permissions != null && permissions.length > 0) + if (!permissions[0].equals("")) { + TextView tvPermissions = (TextView) dlgHelp.findViewById(R.id.tvPermissions); + tvPermissions.setText(Html.fromHtml(TextUtils.join("
", permissions))); + } + + dlgHelp.show(); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityBase.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityBase.java new file mode 100644 index 0000000..d8847cf --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityBase.java @@ -0,0 +1,191 @@ +package biz.bokhorst.xprivacy; + +import android.annotation.SuppressLint; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningServiceInfo; +import android.content.pm.PackageInfo; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Bitmap.Config; +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Process; +import android.support.v7.app.AppCompatActivity; +import android.util.TypedValue; +import android.view.View; +import android.widget.TextView; + +@SuppressLint("Registered") +public class ActivityBase extends AppCompatActivity { + private int mThemeId; + private Bitmap[] mCheck = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + if (PrivacyService.checkClient()) { + // Set theme + int userId = Util.getUserId(Process.myUid()); + String themeName = PrivacyManager.getSetting(userId, PrivacyManager.cSettingTheme, ""); + mThemeId = (themeName.equals("Dark") ? R.style.CustomTheme : R.style.CustomTheme_Light); + setTheme(mThemeId); + } + + super.onCreate(savedInstanceState); + + // Check if Privacy client available + if (!PrivacyService.checkClient()) { + setContentView(R.layout.reboot); + + try { + PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + TextView tvVersion = (TextView) findViewById(R.id.tvVersion); + tvVersion.setText(pInfo.versionName); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + // Show reason + if (PrivacyService.getClient() == null) { + ((TextView) findViewById(R.id.tvRebootClient)).setVisibility(View.VISIBLE); + Requirements.checkCompatibility(this); + } else { + ((TextView) findViewById(R.id.tvRebootVersion)).setVisibility(View.VISIBLE); + Requirements.check(this); + } + + // Show if updating + if (isUpdating()) + ((TextView) findViewById(R.id.tvServiceUpdating)).setVisibility(View.VISIBLE); + } + } + + private boolean isUpdating() { + ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); + for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) + if (UpdateService.class.getName().equals(service.service.getClassName())) + return true; + return false; + } + + protected Bitmap getOffCheckBox() { + if (mCheck == null) + buildCheckBoxes(); + return mCheck[0]; + } + + protected Bitmap getHalfCheckBox() { + if (mCheck == null) + buildCheckBoxes(); + return mCheck[1]; + } + + protected Bitmap getFullCheckBox() { + if (mCheck == null) + buildCheckBoxes(); + return mCheck[2]; + } + + protected Bitmap getOnDemandCheckBox() { + if (mCheck == null) + buildCheckBoxes(); + return mCheck[3]; + } + + protected Bitmap getCheckBoxImage(RState state, boolean expert) { + if (state.partialRestricted) + if (expert) + return getHalfCheckBox(); + else + return getFullCheckBox(); + else if (state.restricted) + return getFullCheckBox(); + else + return getOffCheckBox(); + } + + protected Bitmap getAskBoxImage(RState state, boolean expert) { + if (state.partialAsk) + if (expert) + return getHalfCheckBox(); + else + return getOnDemandCheckBox(); + else if (state.asked) + return getOffCheckBox(); + else + return getOnDemandCheckBox(); + } + + @SuppressWarnings("deprecation") + private void buildCheckBoxes() { + mCheck = new Bitmap[4]; + + int userId = Util.getUserId(Process.myUid()); + String themeName = PrivacyManager.getSetting(userId, PrivacyManager.cSettingTheme, ""); + int colorAccent = getResources().getColor( + themeName.equals("Dark") ? R.color.color_accent_dark : R.color.color_accent_light); + + // Get off check box + TypedArray ta2 = getTheme().obtainStyledAttributes(new int[] { android.R.attr.listChoiceIndicatorMultiple }); + Drawable off = ta2.getDrawable(0); + ta2.recycle(); + off.setBounds(0, 0, off.getIntrinsicWidth(), off.getIntrinsicHeight()); + + // Get check mark + Drawable checkmark = getResources().getDrawable(R.drawable.checkmark); + checkmark.setBounds(0, 0, off.getIntrinsicWidth(), off.getIntrinsicHeight()); + checkmark.setColorFilter(colorAccent, Mode.SRC_ATOP); + + // Get check mark outline + Drawable checkmarkOutline = getResources().getDrawable(R.drawable.checkmark_outline); + checkmarkOutline.setBounds(0, 0, off.getIntrinsicWidth(), off.getIntrinsicHeight()); + + // Create off check box + mCheck[0] = Bitmap.createBitmap(off.getIntrinsicWidth(), off.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas0 = new Canvas(mCheck[0]); + off.draw(canvas0); + + // Create half check box + mCheck[1] = Bitmap.createBitmap(off.getIntrinsicWidth(), off.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas1 = new Canvas(mCheck[1]); + off.draw(canvas1); + Paint paint1 = new Paint(); + paint1.setStyle(Paint.Style.FILL); + paint1.setColor(colorAccent); + float wborder = off.getIntrinsicWidth() / 3f; + float hborder = off.getIntrinsicHeight() / 3f; + canvas1.drawRect(wborder, hborder, off.getIntrinsicWidth() - wborder, off.getIntrinsicHeight() - hborder, + paint1); + + // Create full check box + mCheck[2] = Bitmap.createBitmap(off.getIntrinsicWidth(), off.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas2 = new Canvas(mCheck[2]); + off.draw(canvas2); + checkmark.draw(canvas2); + checkmarkOutline.draw(canvas2); + + // Get question mark + Drawable questionmark = getResources().getDrawable(R.drawable.ondemand); + questionmark.setBounds(0, 0, off.getIntrinsicWidth(), off.getIntrinsicHeight()); + questionmark.setColorFilter(colorAccent, Mode.SRC_ATOP); + + // Get question mark outline + Drawable questionmarkOutline = getResources().getDrawable(R.drawable.questionmark_outline); + questionmarkOutline.setBounds(0, 0, off.getIntrinsicWidth(), off.getIntrinsicHeight()); + + // Create question check box + mCheck[3] = Bitmap.createBitmap(off.getIntrinsicWidth(), off.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas3 = new Canvas(mCheck[3]); + off.draw(canvas3); + questionmark.draw(canvas3); + questionmarkOutline.draw(canvas3); + } + + public int getThemed(int attr) { + TypedValue tv = new TypedValue(); + getTheme().resolveAttribute(attr, tv, true); + return tv.resourceId; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityMain.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityMain.java new file mode 100644 index 0000000..b01cb55 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityMain.java @@ -0,0 +1,2346 @@ +package biz.bokhorst.xprivacy; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Process; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.Window; +import android.view.WindowManager; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.BaseExpandableListAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.EditText; +import android.widget.ExpandableListView; +import android.widget.Filter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.RadioGroup; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.SearchView; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +public class ActivityMain extends ActivityBase implements OnItemSelectedListener { + private Spinner spRestriction = null; + private AppListAdapter mAppAdapter = null; + + private String searchQuery = ""; + private int mSortMode; + private boolean mSortInvert; + private int mProgressWidth = 0; + private int mProgress = 0; + + private Handler mProHandler = new Handler(); + + private static final int SORT_BY_NAME = 0; + private static final int SORT_BY_UID = 1; + private static final int SORT_BY_INSTALL_TIME = 2; + private static final int SORT_BY_UPDATE_TIME = 3; + private static final int SORT_BY_MODIFY_TIME = 4; + private static final int SORT_BY_STATE = 5; + + private static final int ACTIVITY_LICENSE = 0; + private static final int LICENSED = 0x0100; + private static final int NOT_LICENSED = 0x0231; + private static final int RETRY = 0x0123; + + private static final int ERROR_CONTACTING_SERVER = 0x101; + private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; + private static final int ERROR_NON_MATCHING_UID = 0x103; + + public static final String cAction = "Action"; + public static final int cActionRefresh = 1; + + public static final Uri cProUri = Uri.parse("http://www.xprivacy.eu/"); + + private static ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), + new PriorityThreadFactory()); + + private static class PriorityThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.NORM_PRIORITY); + return t; + } + } + + private Comparator mSorter = new Comparator() { + @Override + public int compare(ApplicationInfoEx appInfo0, ApplicationInfoEx appInfo1) { + int sortOrder = mSortInvert ? -1 : 1; + switch (mSortMode) { + case SORT_BY_NAME: + return sortOrder * appInfo0.compareTo(appInfo1); + case SORT_BY_UID: + // Default lowest first + return sortOrder * (appInfo0.getUid() - appInfo1.getUid()); + case SORT_BY_INSTALL_TIME: + // Default newest first + Long iTime0 = appInfo0.getInstallTime(ActivityMain.this); + Long iTime1 = appInfo1.getInstallTime(ActivityMain.this); + return sortOrder * iTime1.compareTo(iTime0); + case SORT_BY_UPDATE_TIME: + // Default newest first + Long uTime0 = appInfo0.getUpdateTime(ActivityMain.this); + Long uTime1 = appInfo1.getUpdateTime(ActivityMain.this); + return sortOrder * uTime1.compareTo(uTime0); + case SORT_BY_MODIFY_TIME: + // Default newest first + Long mTime0 = appInfo0.getModificationTime(ActivityMain.this); + Long mTime1 = appInfo1.getModificationTime(ActivityMain.this); + return sortOrder * mTime1.compareTo(mTime0); + case SORT_BY_STATE: + Integer state0 = appInfo0.getState(ActivityMain.this); + Integer state1 = appInfo1.getState(ActivityMain.this); + if (state0.compareTo(state1) == 0) + return sortOrder * appInfo0.compareTo(appInfo1); + else + return sortOrder * state0.compareTo(state1); + } + return 0; + } + }; + + private boolean mPackageChangeReceiverRegistered = false; + + private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + ActivityMain.this.recreate(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final int userId = Util.getUserId(Process.myUid()); + + // Check privacy service client + if (!PrivacyService.checkClient()) + return; + + // Import license file + if (Intent.ACTION_VIEW.equals(getIntent().getAction())) + if (Util.importProLicense(new File(getIntent().getData().getPath())) != null) + Toast.makeText(this, getString(R.string.menu_pro), Toast.LENGTH_LONG).show(); + + // Set layout + setContentView(R.layout.mainlist); + setSupportActionBar((Toolbar) findViewById(R.id.widgetToolbar)); + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + + // Set sub title + if (Util.hasProLicense(this) != null) + getSupportActionBar().setSubtitle(R.string.menu_pro); + + // Annotate + Meta.annotate(this.getResources()); + + // Get localized restriction name + List listRestrictionName = new ArrayList(PrivacyManager.getRestrictions(this).navigableKeySet()); + listRestrictionName.add(0, getString(R.string.menu_all)); + + // Build spinner adapter + SpinnerAdapter spAdapter = new SpinnerAdapter(this, android.R.layout.simple_spinner_item); + spAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spAdapter.addAll(listRestrictionName); + + // Handle info + ImageView imgInfo = (ImageView) findViewById(R.id.imgInfo); + imgInfo.setOnClickListener(new View.OnClickListener() { + @SuppressLint("SetJavaScriptEnabled") + @Override + public void onClick(View view) { + int position = spRestriction.getSelectedItemPosition(); + if (position != AdapterView.INVALID_POSITION) { + String query = (position == 0 ? "restrictions" : (String) PrivacyManager + .getRestrictions(ActivityMain.this).values().toArray()[position - 1]); + + WebView webview = new WebView(ActivityMain.this); + webview.getSettings().setUserAgentString("Mozilla/5.0"); + // needed for hashtag + webview.getSettings().setJavaScriptEnabled(true); + webview.loadUrl("https://github.com/M66B/XPrivacy#" + query); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityMain.this); + alertDialogBuilder.setTitle((String) spRestriction.getSelectedItem()); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setView(webview); + alertDialogBuilder.setCancelable(true); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + } + }); + + // Setup category spinner + spRestriction = (Spinner) findViewById(R.id.spRestriction); + spRestriction.setAdapter(spAdapter); + spRestriction.setOnItemSelectedListener(this); + int pos = getSelectedCategory(userId); + spRestriction.setSelection(pos); + + // Setup sort + mSortMode = Integer.parseInt(PrivacyManager.getSetting(userId, PrivacyManager.cSettingSortMode, "0")); + mSortInvert = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingSortInverted, false); + + // Start task to get app list + AppListTask appListTask = new AppListTask(); + appListTask.executeOnExecutor(mExecutor, (Object) null); + + // Check environment + Requirements.check(this); + + // Licensing + checkLicense(); + + // Listen for package add/remove + IntentFilter iff = new IntentFilter(); + iff.addAction(Intent.ACTION_PACKAGE_ADDED); + iff.addAction(Intent.ACTION_PACKAGE_REMOVED); + iff.addDataScheme("package"); + registerReceiver(mPackageChangeReceiver, iff); + mPackageChangeReceiverRegistered = true; + + boolean showChangelog = true; + + // First run + if (PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFirstRun, true)) { + showChangelog = false; + optionAbout(); + } + + // Tutorial + if (!PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingTutorialMain, false)) { + showChangelog = false; + ((ScrollView) findViewById(R.id.svTutorialHeader)).setVisibility(View.VISIBLE); + ((ScrollView) findViewById(R.id.svTutorialDetails)).setVisibility(View.VISIBLE); + } + View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + ViewParent parent = view.getParent(); + while (!parent.getClass().equals(ScrollView.class)) + parent = parent.getParent(); + ((View) parent).setVisibility(View.GONE); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingTutorialMain, Boolean.TRUE.toString()); + } + }; + ((Button) findViewById(R.id.btnTutorialHeader)).setOnClickListener(listener); + ((Button) findViewById(R.id.btnTutorialDetails)).setOnClickListener(listener); + + // Legacy + if (!PrivacyManager.cVersion3) { + long now = new Date().getTime(); + String legacy = PrivacyManager.getSetting(userId, PrivacyManager.cSettingLegacy, null); + if (legacy == null || now > Long.parseLong(legacy) + 7 * 24 * 60 * 60 * 1000L) { + showChangelog = false; + PrivacyManager.setSetting(userId, PrivacyManager.cSettingLegacy, Long.toString(now)); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setMessage(R.string.title_update_legacy); + alertDialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Util.viewUri(ActivityMain.this, + Uri.parse("https://github.com/M66B/XPrivacy/blob/master/CHANGELOG.md#xprivacy3")); + } + }); + alertDialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + } + + // Show changelog + if (showChangelog) { + String sVersion = PrivacyManager.getSetting(userId, PrivacyManager.cSettingChangelog, null); + Version changelogVersion = new Version(sVersion == null ? "0.0" : sVersion); + Version currentVersion = new Version(Util.getSelfVersionName(this)); + if (sVersion == null || changelogVersion.compareTo(currentVersion) < 0) + optionChangelog(); + } + } + + @Override + protected void onResume() { + super.onResume(); + + // Update category selection + if (spRestriction != null) { + int userId = Util.getUserId(Process.myUid()); + int pos = getSelectedCategory(userId); + spRestriction.setSelection(pos); + } + + // Update list + if (mAppAdapter != null) + mAppAdapter.notifyDataSetChanged(); + } + + @Override + protected void onNewIntent(Intent intent) { + // Handle clear XPrivacy data (needs UI refresh) + Bundle extras = intent.getExtras(); + if (extras != null && extras.containsKey(cAction) && extras.getInt(cAction) == cActionRefresh) + recreate(); + else { + // Refresh application list + if (mAppAdapter != null) + mAppAdapter.notifyDataSetChanged(); + + // Import pro license + if (Intent.ACTION_VIEW.equals(intent.getAction())) + Util.importProLicense(new File(intent.getData().getPath())); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + if (mPackageChangeReceiverRegistered) { + unregisterReceiver(mPackageChangeReceiver); + mPackageChangeReceiverRegistered = false; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent dataIntent) { + super.onActivityResult(requestCode, resultCode, dataIntent); + + if (requestCode == ACTIVITY_LICENSE) { + // License check + if (dataIntent != null) { + int code = dataIntent.getIntExtra("Code", -1); + int reason = dataIntent.getIntExtra("Reason", -1); + + String sReason; + if (reason == LICENSED) + sReason = "LICENSED"; + else if (reason == NOT_LICENSED) + sReason = "NOT_LICENSED"; + else if (reason == RETRY) + sReason = "RETRY"; + else if (reason == ERROR_CONTACTING_SERVER) + sReason = "ERROR_CONTACTING_SERVER"; + else if (reason == ERROR_INVALID_PACKAGE_NAME) + sReason = "ERROR_INVALID_PACKAGE_NAME"; + else if (reason == ERROR_NON_MATCHING_UID) + sReason = "ERROR_NON_MATCHING_UID"; + else + sReason = Integer.toString(reason); + + Util.log(null, Log.WARN, "Licensing: code=" + code + " reason=" + sReason); + + if (code > 0) { + Util.setPro(true); + invalidateOptionsMenu(); + Toast.makeText(this, getString(R.string.menu_pro), Toast.LENGTH_LONG).show(); + } else if (reason == RETRY) { + Util.setPro(false); + mProHandler.postDelayed(new Runnable() { + @Override + public void run() { + checkLicense(); + } + }, 30 * 1000); + } + } + } + } + + // Filtering + + @Override + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + selectRestriction(pos); + } + + @Override + public void onNothingSelected(AdapterView parent) { + selectRestriction(0); + } + + private void selectRestriction(int pos) { + if (mAppAdapter != null) { + int userId = Util.getUserId(Process.myUid()); + String restrictionName = (pos == 0 ? null : (String) PrivacyManager.getRestrictions(this).values() + .toArray()[pos - 1]); + mAppAdapter.setRestrictionName(restrictionName); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingSelectedCategory, restrictionName); + applyFilter(); + } + } + + private void applyFilter() { + if (mAppAdapter != null) { + ProgressBar pbFilter = (ProgressBar) findViewById(R.id.pbFilter); + TextView tvStats = (TextView) findViewById(R.id.tvStats); + TextView tvState = (TextView) findViewById(R.id.tvState); + + // Get settings + int userId = Util.getUserId(Process.myUid()); + boolean fUsed = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUsed, false); + boolean fInternet = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFInternet, false); + boolean fRestriction = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFRestriction, false); + boolean fRestrictionNot = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFRestrictionNot, + false); + boolean fPermission = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFPermission, true); + boolean fOnDemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFOnDemand, false); + boolean fOnDemandNot = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFOnDemandNot, false); + boolean fUser = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUser, true); + boolean fSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFSystem, false); + + String filter = String.format("%s\n%b\n%b\n%b\n%b\n%b\n%b\n%b\n%b\n%b", searchQuery, fUsed, fInternet, + fRestriction, fRestrictionNot, fPermission, fOnDemand, fOnDemandNot, fUser, fSystem); + pbFilter.setVisibility(ProgressBar.VISIBLE); + tvStats.setVisibility(TextView.GONE); + + // Adjust progress state width + RelativeLayout.LayoutParams tvStateLayout = (RelativeLayout.LayoutParams) tvState.getLayoutParams(); + tvStateLayout.addRule(RelativeLayout.LEFT_OF, R.id.pbFilter); + + mAppAdapter.getFilter().filter(filter); + } + } + + private void applySort() { + if (mAppAdapter != null) + mAppAdapter.sort(); + } + + // Options + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + if (inflater != null && PrivacyService.checkClient()) { + // Inflate menu + inflater.inflate(R.menu.main, menu); + + // Searchable + SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.menu_search)); + if (searchView != null) { + searchView.setIconifiedByDefault(false); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextChange(String newText) { + searchQuery = newText; + applyFilter(); + return true; + } + + @Override + public boolean onQueryTextSubmit(String query) { + searchQuery = query; + applyFilter(); + return true; + } + }); + searchView.setOnCloseListener(new SearchView.OnCloseListener() { + @Override + public boolean onClose() { + searchQuery = ""; + applyFilter(); + return true; + } + }); + } + + return true; + } else + return false; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + int userId = Util.getUserId(Process.myUid()); + boolean mounted = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + boolean updates = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingUpdates, false); + + menu.findItem(R.id.menu_export).setEnabled(mounted); + menu.findItem(R.id.menu_import).setEnabled(mounted); + + menu.findItem(R.id.menu_submit).setEnabled(Util.hasValidFingerPrint(this)); + + menu.findItem(R.id.menu_pro).setVisible(!Util.isProEnabled() && Util.hasProLicense(this) == null); + + menu.findItem(R.id.menu_dump).setVisible(Util.isDebuggable(this)); + + menu.findItem(R.id.menu_update).setVisible(updates); + menu.findItem(R.id.menu_update).setEnabled(mounted); + + // Update filter count + + // Get settings + boolean fUsed = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUsed, false); + boolean fInternet = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFInternet, false); + boolean fRestriction = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFRestriction, false); + boolean fPermission = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFPermission, true); + boolean fOnDemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFOnDemand, false); + boolean fUser = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUser, true); + boolean fSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFSystem, false); + + // Count number of active filters + int numberOfFilters = 0; + if (fUsed) + numberOfFilters++; + if (fInternet) + numberOfFilters++; + if (fRestriction) + numberOfFilters++; + if (fPermission) + numberOfFilters++; + if (fOnDemand) + numberOfFilters++; + if (fUser) + numberOfFilters++; + if (fSystem) + numberOfFilters++; + + if (numberOfFilters > 0) { + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_filter).copy( + Bitmap.Config.ARGB_8888, true); + + Paint paint = new Paint(); + paint.setStyle(Style.FILL); + paint.setColor(Color.GRAY); + paint.setTextSize(bitmap.getWidth() / 3); + paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + + String text = Integer.toString(numberOfFilters); + + Canvas canvas = new Canvas(bitmap); + canvas.drawText(text, bitmap.getWidth() - paint.measureText(text), bitmap.getHeight(), paint); + + MenuItem fMenu = menu.findItem(R.id.menu_filter); + fMenu.setIcon(new BitmapDrawable(getResources(), bitmap)); + } + + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + try { + switch (item.getItemId()) { + case R.id.menu_sort: + optionSort(); + return true; + case R.id.menu_filter: + optionFilter(); + return true; + case R.id.menu_usage: + optionUsage(); + return true; + case R.id.menu_template: + optionTemplate(); + return true; + case R.id.menu_select_all: + optionSelectAll(); + return true; + case R.id.menu_toggle: + optionToggle(); + return true; + case R.id.menu_export: + optionExport(); + return true; + case R.id.menu_import: + optionImport(); + return true; + case R.id.menu_submit: + optionSubmit(); + return true; + case R.id.menu_fetch: + optionFetch(); + return true; + case R.id.menu_pro: + optionPro(); + return true; + case R.id.menu_theme: + optionSwitchTheme(); + return true; + case R.id.menu_settings: + optionSettings(); + return true; + case R.id.menu_dump: + optionDump(); + return true; + case R.id.menu_legend: + optionLegend(); + return true; + case R.id.menu_tutorial: + optionTutorial(); + return true; + case R.id.menu_changelog: + optionChangelog(); + return true; + case R.id.menu_update: + optionUpdate(); + return true; + case R.id.menu_report: + optionReportIssue(); + return true; + case R.id.menu_about: + optionAbout(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } catch (Throwable ex) { + Util.bug(null, ex); + return true; + } + } + + @SuppressLint("InflateParams") + private void optionSort() { + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.sort, null); + final RadioGroup rgSMode = (RadioGroup) view.findViewById(R.id.rgSMode); + final CheckBox cbSInvert = (CheckBox) view.findViewById(R.id.cbSInvert); + + // Initialise controls + switch (mSortMode) { + case SORT_BY_NAME: + rgSMode.check(R.id.rbSName); + break; + case SORT_BY_UID: + rgSMode.check(R.id.rbSUid); + break; + case SORT_BY_INSTALL_TIME: + rgSMode.check(R.id.rbSInstalled); + break; + case SORT_BY_UPDATE_TIME: + rgSMode.check(R.id.rbSUpdated); + break; + case SORT_BY_MODIFY_TIME: + rgSMode.check(R.id.rbSModified); + break; + case SORT_BY_STATE: + rgSMode.check(R.id.rbSState); + break; + } + cbSInvert.setChecked(mSortInvert); + + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityMain.this); + alertDialogBuilder.setTitle(R.string.menu_sort); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setView(view); + alertDialogBuilder.setPositiveButton(ActivityMain.this.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (rgSMode.getCheckedRadioButtonId()) { + case R.id.rbSName: + mSortMode = SORT_BY_NAME; + break; + case R.id.rbSUid: + mSortMode = SORT_BY_UID; + break; + case R.id.rbSInstalled: + mSortMode = SORT_BY_INSTALL_TIME; + break; + case R.id.rbSUpdated: + mSortMode = SORT_BY_UPDATE_TIME; + break; + case R.id.rbSModified: + mSortMode = SORT_BY_MODIFY_TIME; + break; + case R.id.rbSState: + mSortMode = SORT_BY_STATE; + break; + } + mSortInvert = cbSInvert.isChecked(); + + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingSortMode, Integer.toString(mSortMode)); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingSortInverted, + Boolean.toString(mSortInvert)); + + applySort(); + } + }); + alertDialogBuilder.setNegativeButton(ActivityMain.this.getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + @SuppressLint("InflateParams") + private void optionFilter() { + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.filters, null); + final CheckBox cbFUsed = (CheckBox) view.findViewById(R.id.cbFUsed); + final CheckBox cbFInternet = (CheckBox) view.findViewById(R.id.cbFInternet); + final CheckBox cbFPermission = (CheckBox) view.findViewById(R.id.cbFPermission); + final CheckBox cbFRestriction = (CheckBox) view.findViewById(R.id.cbFRestriction); + final CheckBox cbFRestrictionNot = (CheckBox) view.findViewById(R.id.cbFRestrictionNot); + final CheckBox cbFOnDemand = (CheckBox) view.findViewById(R.id.cbFOnDemand); + final CheckBox cbFOnDemandNot = (CheckBox) view.findViewById(R.id.cbFOnDemandNot); + final CheckBox cbFUser = (CheckBox) view.findViewById(R.id.cbFUser); + final CheckBox cbFSystem = (CheckBox) view.findViewById(R.id.cbFSystem); + final Button btnDefault = (Button) view.findViewById(R.id.btnDefault); + + // Get settings + final int userId = Util.getUserId(Process.myUid()); + boolean fUsed = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUsed, false); + boolean fInternet = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFInternet, false); + boolean fPermission = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFPermission, true); + boolean fRestriction = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFRestriction, false); + boolean fRestrictionNot = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFRestrictionNot, false); + boolean fOnDemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFOnDemand, false); + boolean fOnDemandNot = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFOnDemandNot, false); + boolean fUser = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFUser, true); + boolean fSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFSystem, false); + + boolean ondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + + // Setup checkboxes + cbFUsed.setChecked(fUsed); + cbFInternet.setChecked(fInternet); + cbFPermission.setChecked(fPermission); + cbFRestriction.setChecked(fRestriction); + cbFRestrictionNot.setChecked(fRestrictionNot); + cbFOnDemand.setChecked(fOnDemand && ondemand); + cbFOnDemandNot.setChecked(fOnDemandNot && ondemand); + cbFUser.setChecked(fUser); + cbFSystem.setChecked(fSystem); + + cbFRestrictionNot.setEnabled(fRestriction); + + cbFOnDemand.setEnabled(ondemand); + cbFOnDemandNot.setEnabled(fOnDemand && ondemand); + + // Manage user/system filter exclusivity + OnCheckedChangeListener checkListener = new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (buttonView == cbFUser) { + if (isChecked) + cbFSystem.setChecked(false); + } else if (buttonView == cbFSystem) { + if (isChecked) + cbFUser.setChecked(false); + } else if (buttonView == cbFRestriction) + cbFRestrictionNot.setEnabled(cbFRestriction.isChecked()); + else if (buttonView == cbFOnDemand) + cbFOnDemandNot.setEnabled(cbFOnDemand.isChecked()); + } + }; + cbFUser.setOnCheckedChangeListener(checkListener); + cbFSystem.setOnCheckedChangeListener(checkListener); + cbFRestriction.setOnCheckedChangeListener(checkListener); + cbFOnDemand.setOnCheckedChangeListener(checkListener); + + // Clear button + btnDefault.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + cbFUsed.setChecked(false); + cbFInternet.setChecked(false); + cbFPermission.setChecked(true); + cbFRestriction.setChecked(false); + cbFRestrictionNot.setChecked(false); + cbFOnDemand.setChecked(false); + cbFOnDemandNot.setChecked(false); + cbFUser.setChecked(true); + cbFSystem.setChecked(false); + } + }); + + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityMain.this); + alertDialogBuilder.setTitle(R.string.menu_filter); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setView(view); + alertDialogBuilder.setPositiveButton(ActivityMain.this.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFUsed, + Boolean.toString(cbFUsed.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFInternet, + Boolean.toString(cbFInternet.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFRestriction, + Boolean.toString(cbFRestriction.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFRestrictionNot, + Boolean.toString(cbFRestrictionNot.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFPermission, + Boolean.toString(cbFPermission.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFOnDemand, + Boolean.toString(cbFOnDemand.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFOnDemandNot, + Boolean.toString(cbFOnDemandNot.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFUser, + Boolean.toString(cbFUser.isChecked())); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFSystem, + Boolean.toString(cbFSystem.isChecked())); + + invalidateOptionsMenu(); + applyFilter(); + } + }); + alertDialogBuilder.setNegativeButton(ActivityMain.this.getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + private void optionUsage() { + Intent intent = new Intent(this, ActivityUsage.class); + if (mAppAdapter != null && mAppAdapter.getRestrictionName() != null) + intent.putExtra(ActivityUsage.cRestriction, mAppAdapter.getRestrictionName()); + startActivity(intent); + } + + @SuppressLint("InflateParams") + private void optionTemplate() { + final int userId = Util.getUserId(Process.myUid()); + + // Build view + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.template, null); + final Spinner spTemplate = (Spinner) view.findViewById(R.id.spTemplate); + Button btnRename = (Button) view.findViewById(R.id.btnRename); + ExpandableListView elvTemplate = (ExpandableListView) view.findViewById(R.id.elvTemplate); + + // Template selector + final SpinnerAdapter spAdapter = new SpinnerAdapter(this, android.R.layout.simple_spinner_item); + spAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + String defaultName = PrivacyManager.getSetting(userId, Meta.cTypeTemplateName, "0", + getString(R.string.title_default)); + spAdapter.add(defaultName); + for (int i = 1; i <= 4; i++) { + String alternateName = PrivacyManager.getSetting(userId, Meta.cTypeTemplateName, Integer.toString(i), + getString(R.string.title_alternate) + " " + i); + spAdapter.add(alternateName); + } + spTemplate.setAdapter(spAdapter); + + // Template definition + final TemplateListAdapter templateAdapter = new TemplateListAdapter(this, view, R.layout.templateentry); + elvTemplate.setAdapter(templateAdapter); + elvTemplate.setGroupIndicator(null); + + spTemplate.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { + templateAdapter.notifyDataSetChanged(); + } + + @Override + public void onNothingSelected(AdapterView arg0) { + templateAdapter.notifyDataSetChanged(); + } + }); + + btnRename.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (Util.hasProLicense(ActivityMain.this) == null) { + // Redirect to pro page + Util.viewUri(ActivityMain.this, cProUri); + return; + } + + final int templateId = spTemplate.getSelectedItemPosition(); + if (templateId == AdapterView.INVALID_POSITION) + return; + + AlertDialog.Builder dlgRename = new AlertDialog.Builder(spTemplate.getContext()); + dlgRename.setTitle(R.string.title_rename); + + final String original = (templateId == 0 ? getString(R.string.title_default) + : getString(R.string.title_alternate) + " " + templateId); + dlgRename.setMessage(original); + + final EditText input = new EditText(spTemplate.getContext()); + dlgRename.setView(input); + + dlgRename.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String name = input.getText().toString(); + if (TextUtils.isEmpty(name)) { + PrivacyManager.setSetting(userId, Meta.cTypeTemplateName, Integer.toString(templateId), + null); + name = original; + } else { + PrivacyManager.setSetting(userId, Meta.cTypeTemplateName, Integer.toString(templateId), + name); + } + spAdapter.remove(spAdapter.getItem(templateId)); + spAdapter.insert(name, templateId); + spAdapter.notifyDataSetChanged(); + } + }); + + dlgRename.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + // Do nothing + } + }); + + dlgRename.create().show(); + } + }); + + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(R.string.menu_template); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setView(view); + alertDialogBuilder.setPositiveButton(getString(R.string.msg_done), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + private void optionSelectAll() { + // Select all visible apps + if (mAppAdapter != null) + mAppAdapter.selectAllVisible(); + } + + private void optionToggle() { + if (mAppAdapter != null) { + Intent intent = new Intent(ActivityShare.ACTION_TOGGLE); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, + mAppAdapter == null ? new int[0] : mAppAdapter.getSelectedOrVisibleUid(0)); + intent.putExtra(ActivityShare.cRestriction, mAppAdapter.getRestrictionName()); + startActivity(intent); + } + } + + private void optionExport() { + Intent intent = new Intent(ActivityShare.ACTION_EXPORT); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, + mAppAdapter == null ? new int[0] : mAppAdapter.getSelectedOrVisibleUid(AppListAdapter.cSelectAppAll)); + startActivity(intent); + } + + private void optionImport() { + Intent intent = new Intent(ActivityShare.ACTION_IMPORT); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, + mAppAdapter == null ? new int[0] : mAppAdapter.getSelectedOrVisibleUid(0)); + startActivity(intent); + } + + private void optionSubmit() { + if (ActivityShare.registerDevice(this)) { + int[] uid = (mAppAdapter == null ? new int[0] : mAppAdapter + .getSelectedOrVisibleUid(AppListAdapter.cSelectAppNone)); + if (uid.length == 0) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(R.string.msg_select); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } else if (uid.length <= ActivityShare.cSubmitLimit) { + if (mAppAdapter != null) { + Intent intent = new Intent(ActivityShare.ACTION_SUBMIT); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, uid); + startActivity(intent); + } + } else { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(getString(R.string.msg_limit, ActivityShare.cSubmitLimit + 1)); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + } + } + + private void optionFetch() { + if (Util.hasProLicense(this) == null) { + // Redirect to pro page + Util.viewUri(this, cProUri); + } else { + if (mAppAdapter != null) { + + Intent intent = new Intent(ActivityShare.ACTION_FETCH); + intent.putExtra(ActivityShare.cInteractive, true); + intent.putExtra(ActivityShare.cUidList, + mAppAdapter == null ? new int[0] : mAppAdapter.getSelectedOrVisibleUid(0)); + startActivity(intent); + } + } + } + + private void optionPro() { + // Redirect to pro page + Util.viewUri(this, cProUri); + } + + private void optionSwitchTheme() { + int userId = Util.getUserId(Process.myUid()); + String themeName = PrivacyManager.getSetting(userId, PrivacyManager.cSettingTheme, ""); + themeName = (themeName.equals("Dark") ? "Light" : "Dark"); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingTheme, themeName); + this.recreate(); + } + + private void optionSettings() { + Intent intent = new Intent(this, ActivitySettings.class); + startActivity(intent); + } + + private void optionDump() { + try { + PrivacyService.getClient().dump(0); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + private void optionLegend() { + // Show help + Dialog dialog = new Dialog(ActivityMain.this); + dialog.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dialog.setTitle(R.string.menu_legend); + dialog.setContentView(R.layout.legend); + dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, getThemed(R.attr.icon_launcher)); + + ((ImageView) dialog.findViewById(R.id.imgHelpHalf)).setImageBitmap(getHalfCheckBox()); + ((ImageView) dialog.findViewById(R.id.imgHelpOnDemand)).setImageBitmap(getOnDemandCheckBox()); + + for (View child : Util.getViewsByTag((ViewGroup) dialog.findViewById(android.R.id.content), "details")) + child.setVisibility(View.GONE); + + ((LinearLayout) dialog.findViewById(R.id.llUnsafe)).setVisibility(PrivacyManager.cVersion3 ? View.VISIBLE + : View.GONE); + + dialog.setCancelable(true); + dialog.show(); + } + + private void optionTutorial() { + ((ScrollView) findViewById(R.id.svTutorialHeader)).setVisibility(View.VISIBLE); + ((ScrollView) findViewById(R.id.svTutorialDetails)).setVisibility(View.VISIBLE); + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingTutorialMain, Boolean.FALSE.toString()); + + Dialog dlgUsage = new Dialog(this); + dlgUsage.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dlgUsage.setTitle(R.string.title_usage_header); + dlgUsage.setContentView(R.layout.usage); + dlgUsage.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, getThemed(R.attr.icon_launcher)); + dlgUsage.setCancelable(true); + dlgUsage.show(); + } + + private void optionChangelog() { + WebView webview = new WebView(this); + webview.setWebViewClient(new WebViewClient() { + public void onPageFinished(WebView view, String url) { + int userId = Util.getUserId(Process.myUid()); + Version currentVersion = new Version(Util.getSelfVersionName(ActivityMain.this)); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingChangelog, currentVersion.toString()); + } + }); + webview.loadUrl("https://github.com/M66B/XPrivacy/blob/master/CHANGELOG.md"); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(R.string.menu_changelog); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setView(webview); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + private void optionUpdate() { + if (Util.hasProLicense(this) == null) + Util.viewUri(this, ActivityMain.cProUri); + else + new ActivityShare.UpdateTask(this).executeOnExecutor(mExecutor); + } + + private void optionReportIssue() { + // Report issue + Util.viewUri(this, Uri.parse("https://github.com/M66B/XPrivacy#support")); + } + + @SuppressLint("DefaultLocale") + private void optionAbout() { + // About + Dialog dlgAbout = new Dialog(this); + dlgAbout.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dlgAbout.setTitle(R.string.menu_about); + dlgAbout.setContentView(R.layout.about); + dlgAbout.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, getThemed(R.attr.icon_launcher)); + + // Show version + try { + int userId = Util.getUserId(Process.myUid()); + Version currentVersion = new Version(Util.getSelfVersionName(this)); + Version storedVersion = new Version( + PrivacyManager.getSetting(userId, PrivacyManager.cSettingVersion, "0.0")); + boolean migrated = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingMigrated, false); + String versionName = currentVersion.toString(); + if (currentVersion.compareTo(storedVersion) != 0) + versionName += "/" + storedVersion.toString(); + if (!migrated) + versionName += "!"; + int versionCode = Util.getSelfVersionCode(this); + TextView tvVersion = (TextView) dlgAbout.findViewById(R.id.tvVersion); + tvVersion.setText(String.format(getString(R.string.app_version), versionName, versionCode)); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + if (!PrivacyManager.cVersion3 || Hook.isAOSP(19)) + ((TextView) dlgAbout.findViewById(R.id.tvCompatibility)).setVisibility(View.GONE); + + // Show license + String licensed = Util.hasProLicense(this); + TextView tvLicensed1 = (TextView) dlgAbout.findViewById(R.id.tvLicensed); + TextView tvLicensed2 = (TextView) dlgAbout.findViewById(R.id.tvLicensedAlt); + if (licensed == null) { + tvLicensed1.setText(Environment.getExternalStorageDirectory().getAbsolutePath()); + tvLicensed2.setText(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + .getAbsolutePath()); + } else { + tvLicensed1.setText(String.format(getString(R.string.app_licensed), licensed)); + tvLicensed2.setVisibility(View.GONE); + } + + // Show some build properties + String android = String.format("%s (%d)", Build.VERSION.RELEASE, Build.VERSION.SDK_INT); + ((TextView) dlgAbout.findViewById(R.id.tvAndroid)).setText(android); + ((TextView) dlgAbout.findViewById(R.id.build_brand)).setText(Build.BRAND); + ((TextView) dlgAbout.findViewById(R.id.build_manufacturer)).setText(Build.MANUFACTURER); + ((TextView) dlgAbout.findViewById(R.id.build_model)).setText(Build.MODEL); + ((TextView) dlgAbout.findViewById(R.id.build_product)).setText(Build.PRODUCT); + ((TextView) dlgAbout.findViewById(R.id.build_device)).setText(Build.DEVICE); + ((TextView) dlgAbout.findViewById(R.id.build_host)).setText(Build.HOST); + ((TextView) dlgAbout.findViewById(R.id.build_display)).setText(Build.DISPLAY); + ((TextView) dlgAbout.findViewById(R.id.build_id)).setText(Build.ID); + + dlgAbout.setCancelable(true); + + final int userId = Util.getUserId(Process.myUid()); + if (PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingFirstRun, true)) + dlgAbout.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + Dialog dlgUsage = new Dialog(ActivityMain.this); + dlgUsage.requestWindowFeature(Window.FEATURE_LEFT_ICON); + dlgUsage.setTitle(R.string.app_name); + dlgUsage.setContentView(R.layout.usage); + dlgUsage.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, getThemed(R.attr.icon_launcher)); + dlgUsage.setCancelable(true); + dlgUsage.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + PrivacyManager.setSetting(userId, PrivacyManager.cSettingFirstRun, Boolean.FALSE.toString()); + optionLegend(); + } + }); + dlgUsage.show(); + } + }); + + dlgAbout.show(); + } + + // Tasks + + private class AppListTask extends AsyncTask> { + private String mRestrictionName; + private ProgressDialog mProgressDialog; + + @Override + protected List doInBackground(Object... params) { + mRestrictionName = null; + + // Delegate + return ApplicationInfoEx.getXApplicationList(ActivityMain.this, mProgressDialog); + } + + @SuppressWarnings("deprecation") + @Override + protected void onPreExecute() { + super.onPreExecute(); + + TypedArray ta = getTheme().obtainStyledAttributes(new int[] { R.attr.progress_horizontal }); + int progress_horizontal = ta.getResourceId(0, 0); + ta.recycle(); + + // Show progress dialog + mProgressDialog = new ProgressDialog(ActivityMain.this); + mProgressDialog.setMessage(getString(R.string.msg_loading)); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + mProgressDialog.setProgressDrawable(getResources().getDrawable(progress_horizontal)); + mProgressDialog.setProgressNumberFormat(null); + mProgressDialog.setCancelable(false); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressDialog.show(); + } + + @Override + protected void onPostExecute(List listApp) { + if (!ActivityMain.this.isFinishing()) { + // Display app list + mAppAdapter = new AppListAdapter(ActivityMain.this, R.layout.mainentry, listApp, mRestrictionName); + ListView lvApp = (ListView) findViewById(R.id.lvApp); + lvApp.setAdapter(mAppAdapter); + + // Dismiss progress dialog + if (mProgressDialog.isShowing()) + try { + mProgressDialog.dismiss(); + } catch (IllegalArgumentException ignored) { + } + + // Restore state + ActivityMain.this.selectRestriction(spRestriction.getSelectedItemPosition()); + } + + super.onPostExecute(listApp); + } + } + + // Adapters + + private class SpinnerAdapter extends ArrayAdapter { + public SpinnerAdapter(Context context, int textViewResourceId) { + super(context, textViewResourceId); + } + } + + @SuppressLint("DefaultLocale") + private class TemplateListAdapter extends BaseExpandableListAdapter { + private View mView; + private Spinner mSpinner; + private List listRestrictionName; + private List listLocalizedTitle; + private boolean ondemand; + private Version version; + private LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + public TemplateListAdapter(Context context, View view, int resource) { + mView = view; + mSpinner = (Spinner) view.findViewById(R.id.spTemplate); + + // Get restriction categories + TreeMap tmRestriction = PrivacyManager.getRestrictions(context); + listRestrictionName = new ArrayList(tmRestriction.values()); + listLocalizedTitle = new ArrayList(tmRestriction.navigableKeySet()); + + int userId = Util.getUserId(Process.myUid()); + ondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + version = new Version(Util.getSelfVersionName(context)); + } + + private String getTemplate() { + if (mSpinner.getSelectedItemPosition() == 0) + return Meta.cTypeTemplate; + else + return Meta.cTypeTemplate + mSpinner.getSelectedItemPosition(); + } + + private class ViewHolder { + private View row; + public ImageView imgIndicator; + public ImageView imgInfo; + public TextView tvRestriction; + public ImageView imgUnsafe; + public ImageView imgCbRestrict; + public ImageView imgCbAsk; + public boolean restricted; + public boolean asked; + + public ViewHolder(View theRow) { + row = theRow; + imgIndicator = (ImageView) row.findViewById(R.id.imgIndicator); + imgInfo = (ImageView) row.findViewById(R.id.imgInfo); + tvRestriction = (TextView) row.findViewById(R.id.tvRestriction); + imgUnsafe = (ImageView) row.findViewById(R.id.imgUnsafe); + imgCbRestrict = (ImageView) row.findViewById(R.id.imgCbRestrict); + imgCbAsk = (ImageView) row.findViewById(R.id.imgCbAsk); + } + } + + @Override + public Object getGroup(int groupPosition) { + return listRestrictionName.get(groupPosition); + } + + @Override + public int getGroupCount() { + return listRestrictionName.size(); + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + @SuppressLint("InflateParams") + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.templateentry, null); + holder = new ViewHolder(convertView); + convertView.setTag(holder); + } else + holder = (ViewHolder) convertView.getTag(); + + // Get entry + final String restrictionName = (String) getGroup(groupPosition); + + // Get info + final int userId = Util.getUserId(Process.myUid()); + String value = PrivacyManager.getSetting(userId, getTemplate(), restrictionName, + Boolean.toString(!ondemand) + "+ask"); + holder.restricted = value.contains("true"); + holder.asked = (!ondemand || value.contains("asked")); + + boolean partialRestricted = false; + boolean partialAsked = false; + if (holder.restricted || !holder.asked) + for (Hook hook : PrivacyManager.getHooks(restrictionName, version)) { + String settingName = restrictionName + "." + hook.getName(); + String childValue = PrivacyManager.getSetting(userId, getTemplate(), settingName, null); + if (childValue == null) + childValue = Boolean.toString(holder.restricted && !hook.isDangerous()) + + (holder.asked || (hook.isDangerous() && hook.whitelist() == null) ? "+asked" : "+ask"); + if (!childValue.contains("true")) + partialRestricted = true; + if (childValue.contains("asked")) + partialAsked = true; + } + + Bitmap bmRestricted = (holder.restricted ? partialRestricted ? getHalfCheckBox() : getFullCheckBox() + : getOffCheckBox()); + Bitmap bmAsked = (holder.asked ? getOffCheckBox() : partialAsked ? getHalfCheckBox() + : getOnDemandCheckBox()); + + // Indicator state + holder.imgIndicator.setImageResource(getThemed(isExpanded ? R.attr.icon_expander_maximized + : R.attr.icon_expander_minimized)); + holder.imgIndicator.setVisibility(View.VISIBLE); + holder.imgInfo.setVisibility(View.GONE); + holder.imgUnsafe.setVisibility(View.GONE); + + // Set data + holder.tvRestriction.setTypeface(null, Typeface.BOLD); + holder.tvRestriction.setText(listLocalizedTitle.get(groupPosition)); + holder.imgCbRestrict.setImageBitmap(bmRestricted); + holder.imgCbAsk.setImageBitmap(bmAsked); + holder.imgCbAsk.setVisibility(ondemand ? View.VISIBLE : View.GONE); + + holder.imgCbRestrict.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + // Update setting + holder.restricted = !holder.restricted; + PrivacyManager.setSetting(userId, getTemplate(), restrictionName, (holder.restricted ? "true" + : "false") + "+" + (holder.asked ? "asked" : "ask")); + notifyDataSetChanged(); // update childs + } + }); + + holder.imgCbAsk.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + // Update setting + holder.asked = (!ondemand || !holder.asked); + PrivacyManager.setSetting(userId, getTemplate(), restrictionName, (holder.restricted ? "true" + : "false") + "+" + (holder.asked ? "asked" : "ask")); + notifyDataSetChanged(); // update childs + } + }); + + return convertView; + } + + @Override + public Object getChild(int groupPosition, int childPosition) { + return PrivacyManager.getHooks((String) getGroup(groupPosition), version).get(childPosition); + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return childPosition; + } + + @Override + public int getChildrenCount(int groupPosition) { + return PrivacyManager.getHooks((String) getGroup(groupPosition), version).size(); + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return false; + } + + @Override + @SuppressLint("InflateParams") + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, + ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.templateentry, null); + holder = new ViewHolder(convertView); + convertView.setTag(holder); + } else + holder = (ViewHolder) convertView.getTag(); + + // Get entry + final int userId = Util.getUserId(Process.myUid()); + final String restrictionName = (String) getGroup(groupPosition); + final Hook hook = (Hook) getChild(groupPosition, childPosition); + final String settingName = restrictionName + "." + hook.getName(); + + // Get parent info + String parentValue = PrivacyManager.getSetting(userId, getTemplate(), restrictionName, + Boolean.toString(!ondemand) + "+ask"); + boolean parentRestricted = parentValue.contains("true"); + boolean parentAsked = (!ondemand || parentValue.contains("asked")); + + // Get child info + String value = PrivacyManager.getSetting(userId, getTemplate(), settingName, null); + // This is to circumvent caching problems + // The child value depends on the parent value + if (value == null) + value = Boolean.toString(parentRestricted && !hook.isDangerous()) + + (parentAsked || (hook.isDangerous() && hook.whitelist() == null) ? "+asked" : "+ask"); + holder.restricted = value.contains("true"); + holder.asked = (!ondemand || value.contains("asked")); + Bitmap bmRestricted = (parentRestricted && holder.restricted ? getFullCheckBox() : getOffCheckBox()); + Bitmap bmAsked = (parentAsked || holder.asked ? getOffCheckBox() : getOnDemandCheckBox()); + + // Set indicator + holder.imgIndicator.setVisibility(View.INVISIBLE); + + // Function help + if (hook.getAnnotation() == null) + holder.imgInfo.setVisibility(View.GONE); + else { + holder.imgInfo.setVisibility(View.VISIBLE); + holder.imgInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ActivityApp.showHelp(ActivityMain.this, mView, hook); + } + }); + } + holder.imgUnsafe.setVisibility(hook.isUnsafe() ? View.VISIBLE : View.GONE); + + // Set data + if (hook.isDangerous()) + holder.row.setBackgroundColor(getResources().getColor( + getThemed(hook.isDangerousDefined() ? R.attr.color_dangerous : R.attr.color_dangerous_user))); + else + holder.row.setBackgroundColor(hook.isDangerousDefined() ? getResources().getColor( + getThemed(R.attr.color_dangerous_off)) : Color.TRANSPARENT); + holder.tvRestriction.setText(hook.getName()); + holder.imgCbRestrict.setEnabled(parentRestricted); + holder.imgCbRestrict.setImageBitmap(bmRestricted); + holder.imgCbAsk.setEnabled(!parentAsked); + holder.imgCbAsk.setImageBitmap(bmAsked); + holder.imgCbAsk.setVisibility(ondemand ? View.VISIBLE : View.GONE); + + // Listen for long press + if (Util.getUserId(Process.myUid()) == 0) + holder.tvRestriction.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + hook.toggleDangerous(); + + // Change background color + if (hook.isDangerous()) + holder.row.setBackgroundColor(getResources().getColor( + getThemed(hook.isDangerousDefined() ? R.attr.color_dangerous + : R.attr.color_dangerous_user))); + else + holder.row.setBackgroundColor(hook.isDangerousDefined() ? getResources().getColor( + getThemed(R.attr.color_dangerous_off)) : Color.TRANSPARENT); + + notifyDataSetChanged(); + + return true; + } + }); + + holder.imgCbRestrict.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + // Update setting + holder.restricted = !holder.restricted; + PrivacyManager.setSetting(userId, getTemplate(), settingName, + (holder.restricted ? "true" : "false") + "+" + (holder.asked ? "asked" : "ask")); + notifyDataSetChanged(); // update parent + } + }); + + holder.imgCbAsk.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + // Update setting + holder.asked = !holder.asked; + PrivacyManager.setSetting(userId, getTemplate(), settingName, + (holder.restricted ? "true" : "false") + "+" + (holder.asked ? "asked" : "ask")); + notifyDataSetChanged(); // update parent + } + }); + + return convertView; + } + + @Override + public boolean hasStableIds() { + return true; + } + } + + @SuppressLint("DefaultLocale") + private class AppListAdapter extends ArrayAdapter { + private Context mContext; + private boolean mSelecting = false; + private List mListAppAll; + private List mListAppSelected = new ArrayList(); + private String mRestrictionName; + private Version mVersion; + private LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + private AtomicInteger mFiltersRunning = new AtomicInteger(0); + private int mHighlightColor; + + public final static int cSelectAppAll = 1; + public final static int cSelectAppNone = 2; + + public AppListAdapter(Context context, int resource, List objects, + String initialRestrictionName) { + super(context, resource, objects); + mContext = context; + mListAppAll = new ArrayList(); + mListAppAll.addAll(objects); + mRestrictionName = initialRestrictionName; + mVersion = new Version(Util.getSelfVersionName(context)); + + TypedArray ta1 = context.getTheme().obtainStyledAttributes( + new int[] { android.R.attr.colorPressedHighlight }); + mHighlightColor = ta1.getColor(0, 0xFF00FF); + ta1.recycle(); + } + + public void setRestrictionName(String restrictionName) { + mRestrictionName = restrictionName; + } + + public String getRestrictionName() { + return mRestrictionName; + } + + public List getSelectedOrVisible(int flags) { + if (mListAppSelected.size() > 0) + return mListAppSelected; + else { + if (flags == cSelectAppAll) + return mListAppAll; + else { + List listApp = new ArrayList(); + if (flags != cSelectAppNone) + for (int i = 0; i < this.getCount(); i++) + listApp.add(this.getItem(i)); + return listApp; + } + } + } + + public int[] getSelectedOrVisibleUid(int flags) { + List listAppInfo = getSelectedOrVisible(flags); + int[] uid = new int[listAppInfo.size()]; + for (int pos = 0; pos < listAppInfo.size(); pos++) + uid[pos] = listAppInfo.get(pos).getUid(); + return uid; + } + + public void selectAllVisible() { + // Look through the visible apps to figure out what to do + mSelecting = false; + for (int i = 0; i < this.getCount(); i++) { + if (!mListAppSelected.contains(this.getItem(i))) { + mSelecting = true; + break; + } + } + + if (mSelecting) { + // Add the visible apps not already selected + for (int i = 0; i < this.getCount(); i++) + if (!mListAppSelected.contains(this.getItem(i))) + mListAppSelected.add(this.getItem(i)); + } else + mListAppSelected.clear(); + + this.showStats(); + this.notifyDataSetChanged(); + } + + public void showStats() { + TextView tvStats = (TextView) findViewById(R.id.tvStats); + String stats = String.format("%d/%d", this.getCount(), mListAppAll.size()); + if (mListAppSelected.size() > 0) + stats += String.format(" (%d)", mListAppSelected.size()); + tvStats.setText(stats); + } + + @Override + public Filter getFilter() { + return new AppFilter(); + } + + private class AppFilter extends Filter { + public AppFilter() { + } + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + int userId = Util.getUserId(Process.myUid()); + + int filtersRunning = mFiltersRunning.addAndGet(1); + FilterResults results = new FilterResults(); + + // Get arguments + String[] components = ((String) constraint).split("\\n"); + String fName = components[0]; + boolean fUsed = Boolean.parseBoolean(components[1]); + boolean fInternet = Boolean.parseBoolean(components[2]); + boolean fRestricted = Boolean.parseBoolean(components[3]); + boolean fRestrictedNot = Boolean.parseBoolean(components[4]); + boolean fPermission = Boolean.parseBoolean(components[5]); + boolean fOnDemand = Boolean.parseBoolean(components[6]); + boolean fOnDemandNot = Boolean.parseBoolean(components[7]); + boolean fUser = Boolean.parseBoolean(components[8]); + boolean fSystem = Boolean.parseBoolean(components[9]); + + // Match applications + int current = 0; + int max = AppListAdapter.this.mListAppAll.size(); + List lstApp = new ArrayList(); + for (ApplicationInfoEx xAppInfo : AppListAdapter.this.mListAppAll) { + // Check if another filter has been started + if (filtersRunning != mFiltersRunning.get()) + return null; + + // Send progress info to main activity + current++; + if (current % 5 == 0) { + final int position = current; + final int maximum = max; + runOnUiThread(new Runnable() { + @Override + public void run() { + setProgress(getString(R.string.msg_applying), position, maximum); + } + }); + } + + // Get if name contains + boolean contains = false; + if (!fName.equals("")) + contains = (xAppInfo.toString().toLowerCase().contains(((String) fName).toLowerCase())); + + // Get if used + boolean used = false; + if (fUsed) + used = (PrivacyManager.getUsage(xAppInfo.getUid(), mRestrictionName, null) != 0); + + // Get if internet + boolean internet = false; + if (fInternet) + internet = xAppInfo.hasInternet(mContext); + + // Get some restricted + boolean someRestricted = false; + if (fRestricted) + for (PRestriction restriction : PrivacyManager.getRestrictionList(xAppInfo.getUid(), + mRestrictionName)) + if (restriction.restricted) { + someRestricted = true; + break; + } + + // Get Android permission + boolean permission = false; + if (fPermission) + if (mRestrictionName == null) + permission = true; + else if (PrivacyManager.hasPermission(mContext, xAppInfo, mRestrictionName, mVersion) + || PrivacyManager.getUsage(xAppInfo.getUid(), mRestrictionName, null) > 0) + permission = true; + + // Get if onDemand + boolean onDemand = false; + boolean isApp = PrivacyManager.isApplication(xAppInfo.getUid()); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, + false); + boolean gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + if (fOnDemand && (isApp || odSystem) && gondemand) { + onDemand = PrivacyManager.getSettingBool(-xAppInfo.getUid(), PrivacyManager.cSettingOnDemand, + false); + if (onDemand && mRestrictionName != null) + onDemand = !PrivacyManager.getRestrictionEx(xAppInfo.getUid(), mRestrictionName, null).asked; + } + + // Get if user + boolean user = false; + if (fUser) + user = !xAppInfo.isSystem(); + + // Get if system + boolean system = false; + if (fSystem) + system = xAppInfo.isSystem(); + + // Apply filters + if ((fName.equals("") ? true : contains) && (fUsed ? used : true) && (fInternet ? internet : true) + && (fRestricted ? (fRestrictedNot ? !someRestricted : someRestricted) : true) + && (fPermission ? permission : true) + && (fOnDemand ? (fOnDemandNot ? !onDemand : onDemand) : true) && (fUser ? user : true) + && (fSystem ? system : true)) + lstApp.add(xAppInfo); + } + + // Check again whether another filter has been started + if (filtersRunning != mFiltersRunning.get()) + return null; + + // Apply current sorting + Collections.sort(lstApp, mSorter); + + // Last check whether another filter has been started + if (filtersRunning != mFiltersRunning.get()) + return null; + + synchronized (this) { + results.values = lstApp; + results.count = lstApp.size(); + } + + return results; + } + + @Override + @SuppressWarnings("unchecked") + protected void publishResults(CharSequence constraint, FilterResults results) { + if (results != null) { + clear(); + TextView tvStats = (TextView) findViewById(R.id.tvStats); + TextView tvState = (TextView) findViewById(R.id.tvState); + ProgressBar pbFilter = (ProgressBar) findViewById(R.id.pbFilter); + pbFilter.setVisibility(ProgressBar.GONE); + tvStats.setVisibility(TextView.VISIBLE); + + runOnUiThread(new Runnable() { + @Override + public void run() { + setProgress(getString(R.string.title_restrict), 0, 1); + } + }); + + // Adjust progress state width + RelativeLayout.LayoutParams tvStateLayout = (RelativeLayout.LayoutParams) tvState.getLayoutParams(); + tvStateLayout.addRule(RelativeLayout.LEFT_OF, R.id.tvStats); + + if (results.values == null) + notifyDataSetInvalidated(); + else { + addAll((ArrayList) results.values); + notifyDataSetChanged(); + } + AppListAdapter.this.showStats(); + } + } + } + + public void sort() { + sort(mSorter); + } + + private class ViewHolder { + private View row; + private int position; + public View vwState; + public LinearLayout llAppType; + public ImageView imgIcon; + public ImageView imgUsed; + public ImageView imgGranted; + public ImageView imgInternet; + public ImageView imgFrozen; + public ImageView imgSettings; + public LinearLayout llName; + public TextView tvName; + public ImageView imgCbRestricted; + public ProgressBar pbRunning; + public ImageView imgCbAsk; + + public ViewHolder(View theRow, int thePosition) { + row = theRow; + position = thePosition; + vwState = (View) row.findViewById(R.id.vwState); + llAppType = (LinearLayout) row.findViewById(R.id.llAppType); + imgIcon = (ImageView) row.findViewById(R.id.imgIcon); + imgUsed = (ImageView) row.findViewById(R.id.imgUsed); + imgGranted = (ImageView) row.findViewById(R.id.imgGranted); + imgInternet = (ImageView) row.findViewById(R.id.imgInternet); + imgFrozen = (ImageView) row.findViewById(R.id.imgFrozen); + imgSettings = (ImageView) row.findViewById(R.id.imgSettings); + llName = (LinearLayout) row.findViewById(R.id.llName); + tvName = (TextView) row.findViewById(R.id.tvName); + imgCbRestricted = (ImageView) row.findViewById(R.id.imgCbRestricted); + pbRunning = (ProgressBar) row.findViewById(R.id.pbRunning); + imgCbAsk = (ImageView) row.findViewById(R.id.imgCbAsk); + } + } + + private class HolderTask extends AsyncTask { + private int position; + private ViewHolder holder; + private ApplicationInfoEx xAppInfo = null; + private int state; + private Bitmap bicon; + private Drawable dicon; + private boolean used; + private boolean enabled; + private boolean granted; + private boolean settings; + private RState rstate; + private boolean gondemand; + private boolean ondemand; + private boolean can; + private boolean methodExpert; + + public HolderTask(int thePosition, ViewHolder theHolder, ApplicationInfoEx theAppInfo) { + position = thePosition; + holder = theHolder; + xAppInfo = theAppInfo; + } + + @Override + protected Object doInBackground(Object... params) { + if (xAppInfo != null) { + int userId = Util.getUserId(Process.myUid()); + + // Get state + state = xAppInfo.getState(ActivityMain.this); + + // Get icon + bicon = xAppInfo.getIconBitmap(ActivityMain.this); + if (bicon == null) + dicon = xAppInfo.getIcon(ActivityMain.this); + + // Get if used + used = (PrivacyManager.getUsage(xAppInfo.getUid(), mRestrictionName, null) != 0); + + // Get if enabled + enabled = PrivacyManager.getSettingBool(xAppInfo.getUid(), PrivacyManager.cSettingRestricted, true); + + // Get if on demand + gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + boolean isApp = PrivacyManager.isApplication(xAppInfo.getUid()); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, + false); + ondemand = (isApp || odSystem); + if (ondemand && mRestrictionName != null) + ondemand = PrivacyManager.getSettingBool(-xAppInfo.getUid(), PrivacyManager.cSettingOnDemand, + false); + + // Get if granted + granted = true; + if (mRestrictionName != null) + if (!PrivacyManager.hasPermission(ActivityMain.this, xAppInfo, mRestrictionName, mVersion)) + granted = false; + + // Get if application settings + settings = PrivacyManager.hasSpecificSettings(xAppInfo.getUid()); + + // Get restriction/ask state + rstate = new RState(xAppInfo.getUid(), mRestrictionName, null, mVersion); + + // Get can restrict + can = PrivacyManager.canRestrict(rstate.mUid, Process.myUid(), rstate.mRestrictionName, + rstate.mMethodName, true); + methodExpert = (mRestrictionName == null || PrivacyManager.getSettingBool(userId, + PrivacyManager.cSettingMethodExpert, false)); + + return holder; + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (holder.position == position && result != null) { + // Set background color + if (xAppInfo.isSystem()) + holder.llAppType.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_dangerous))); + else + holder.llAppType.setBackgroundColor(Color.TRANSPARENT); + + // Display state + if (state == ApplicationInfoEx.STATE_ATTENTION) + holder.vwState.setBackgroundColor(getResources().getColor( + getThemed(R.attr.color_state_attention))); + else if (state == ApplicationInfoEx.STATE_SHARED) + holder.vwState + .setBackgroundColor(getResources().getColor(getThemed(R.attr.color_state_shared))); + else + holder.vwState.setBackgroundColor(getResources().getColor( + getThemed(R.attr.color_state_restricted))); + + // Display icon + if (bicon == null) + holder.imgIcon.setImageDrawable(dicon); + else + holder.imgIcon.setImageBitmap(bicon); + holder.imgIcon.setVisibility(View.VISIBLE); + + // Display on demand + if (gondemand) { + if (ondemand) { + holder.imgCbAsk.setImageBitmap(getAskBoxImage(rstate, methodExpert)); + holder.imgCbAsk.setVisibility(View.VISIBLE); + } else + holder.imgCbAsk.setVisibility(View.INVISIBLE); + } else + holder.imgCbAsk.setVisibility(View.GONE); + + // Display usage + holder.tvName.setTypeface(null, used ? Typeface.BOLD_ITALIC : Typeface.NORMAL); + holder.imgUsed.setVisibility(used ? View.VISIBLE : View.INVISIBLE); + + // Display if permissions + holder.imgGranted.setVisibility(granted ? View.VISIBLE : View.INVISIBLE); + + // Display if internet access + holder.imgInternet.setVisibility(xAppInfo.hasInternet(ActivityMain.this) ? View.VISIBLE + : View.INVISIBLE); + + // Display if frozen + holder.imgFrozen + .setVisibility(xAppInfo.isFrozen(ActivityMain.this) ? View.VISIBLE : View.INVISIBLE); + + // Display if settings + holder.imgSettings.setVisibility(settings ? View.VISIBLE : View.GONE); + + // Display restriction + holder.imgCbRestricted.setImageBitmap(getCheckBoxImage(rstate, methodExpert)); + holder.imgCbRestricted.setVisibility(View.VISIBLE); + + // Display enabled state + holder.tvName.setEnabled(enabled && can); + holder.imgCbRestricted.setEnabled(enabled && can); + holder.imgCbAsk.setEnabled(enabled && can); + + // Display selection + if (mListAppSelected.contains(xAppInfo)) + holder.row.setBackgroundColor(mHighlightColor); + else + holder.row.setBackgroundColor(Color.TRANSPARENT); + + // Handle details click + holder.imgIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intentSettings = new Intent(ActivityMain.this, ActivityApp.class); + intentSettings.putExtra(ActivityApp.cUid, xAppInfo.getUid()); + intentSettings.putExtra(ActivityApp.cRestrictionName, mRestrictionName); + ActivityMain.this.startActivity(intentSettings); + } + }); + + // Listen for restriction changes + holder.imgCbRestricted.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mRestrictionName == null && rstate.restricted != false) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityMain.this); + alertDialogBuilder.setTitle(R.string.menu_clear_all); + alertDialogBuilder.setMessage(R.string.msg_sure); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + deleteRestrictions(); + } + }); + alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } else + toggleRestrictions(); + } + }); + + // Listen for ask changes + if (gondemand && ondemand) + holder.imgCbAsk.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + holder.imgCbAsk.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + @Override + protected Object doInBackground(Object... arg0) { + rstate.toggleAsked(); + rstate = new RState(xAppInfo.getUid(), mRestrictionName, null, mVersion); + return null; + } + + @Override + protected void onPostExecute(Object result) { + holder.imgCbAsk.setImageBitmap(getAskBoxImage(rstate, methodExpert)); + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbAsk.setVisibility(View.VISIBLE); + } + }.executeOnExecutor(mExecutor); + } + }); + else + holder.imgCbAsk.setClickable(false); + } + } + + private void deleteRestrictions() { + holder.imgCbRestricted.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + private List oldState; + + @Override + protected Object doInBackground(Object... arg0) { + // Update restriction + oldState = PrivacyManager.getRestartStates(xAppInfo.getUid(), mRestrictionName); + PrivacyManager.deleteRestrictions(xAppInfo.getUid(), null, true); + PrivacyManager.setSetting(xAppInfo.getUid(), PrivacyManager.cSettingOnDemand, + Boolean.toString(true)); + return null; + } + + @Override + protected void onPostExecute(Object result) { + // Update visible state + holder.vwState.setBackgroundColor(getResources().getColor( + getThemed(R.attr.color_state_attention))); + + // Update stored state + rstate = new RState(xAppInfo.getUid(), mRestrictionName, null, mVersion); + holder.imgCbRestricted.setImageBitmap(getCheckBoxImage(rstate, methodExpert)); + holder.imgCbAsk.setImageBitmap(getAskBoxImage(rstate, methodExpert)); + + // Notify restart + if (oldState.contains(true)) + Toast.makeText(ActivityMain.this, getString(R.string.msg_restart), Toast.LENGTH_LONG) + .show(); + + // Display new state + showState(); + + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbRestricted.setVisibility(View.VISIBLE); + } + }.executeOnExecutor(mExecutor); + } + + private void toggleRestrictions() { + holder.imgCbRestricted.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + + new AsyncTask() { + private List oldState; + private List newState; + + @Override + protected Object doInBackground(Object... arg0) { + // Change restriction + oldState = PrivacyManager.getRestartStates(xAppInfo.getUid(), mRestrictionName); + rstate.toggleRestriction(); + newState = PrivacyManager.getRestartStates(xAppInfo.getUid(), mRestrictionName); + return null; + } + + @Override + protected void onPostExecute(Object result) { + // Update restriction display + rstate = new RState(xAppInfo.getUid(), mRestrictionName, null, mVersion); + holder.imgCbRestricted.setImageBitmap(getCheckBoxImage(rstate, methodExpert)); + holder.imgCbAsk.setImageBitmap(getAskBoxImage(rstate, methodExpert)); + + // Notify restart + if (!newState.equals(oldState)) + Toast.makeText(ActivityMain.this, getString(R.string.msg_restart), Toast.LENGTH_LONG) + .show(); + + // Display new state + showState(); + + holder.pbRunning.setVisibility(View.GONE); + holder.imgCbRestricted.setVisibility(View.VISIBLE); + } + }.executeOnExecutor(mExecutor); + } + + private void showState() { + state = xAppInfo.getState(ActivityMain.this); + if (state == ApplicationInfoEx.STATE_ATTENTION) + holder.vwState.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_state_attention))); + else if (state == ApplicationInfoEx.STATE_SHARED) + holder.vwState.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_state_shared))); + else + holder.vwState + .setBackgroundColor(getResources().getColor(getThemed(R.attr.color_state_restricted))); + } + } + + @Override + @SuppressLint("InflateParams") + public View getView(int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.mainentry, null); + holder = new ViewHolder(convertView, position); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + holder.position = position; + } + + // Get info + final ApplicationInfoEx xAppInfo = getItem(holder.position); + + // Set data + holder.row.setBackgroundColor(Color.TRANSPARENT); + holder.vwState.setBackgroundColor(Color.TRANSPARENT); + holder.llAppType.setBackgroundColor(Color.TRANSPARENT); + holder.imgIcon.setVisibility(View.INVISIBLE); + holder.tvName.setText(xAppInfo.toString()); + holder.tvName.setTypeface(null, Typeface.NORMAL); + holder.imgUsed.setVisibility(View.INVISIBLE); + holder.imgGranted.setVisibility(View.INVISIBLE); + holder.imgInternet.setVisibility(View.INVISIBLE); + holder.imgFrozen.setVisibility(View.INVISIBLE); + holder.imgSettings.setVisibility(View.GONE); + holder.imgCbRestricted.setVisibility(View.INVISIBLE); + holder.imgCbAsk.setVisibility(View.INVISIBLE); + holder.tvName.setEnabled(false); + holder.imgCbRestricted.setEnabled(false); + + holder.imgIcon.setClickable(false); + holder.imgCbRestricted.setClickable(false); + holder.imgCbAsk.setClickable(false); + + // Listen for multiple select + holder.llName.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + if (mListAppSelected.contains(xAppInfo)) { + mSelecting = false; + mListAppSelected.clear(); + mAppAdapter.notifyDataSetChanged(); + } else { + mSelecting = true; + mListAppSelected.add(xAppInfo); + holder.row.setBackgroundColor(mHighlightColor); + } + showStats(); + return true; + } + }); + + // Listen for application selection + holder.llName.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View view) { + if (mSelecting) { + if (mListAppSelected.contains(xAppInfo)) { + mListAppSelected.remove(xAppInfo); + holder.row.setBackgroundColor(Color.TRANSPARENT); + if (mListAppSelected.size() == 0) + mSelecting = false; + } else { + mListAppSelected.add(xAppInfo); + holder.row.setBackgroundColor(mHighlightColor); + } + showStats(); + } else { + Intent intentSettings = new Intent(ActivityMain.this, ActivityApp.class); + intentSettings.putExtra(ActivityApp.cUid, xAppInfo.getUid()); + intentSettings.putExtra(ActivityApp.cRestrictionName, mRestrictionName); + ActivityMain.this.startActivity(intentSettings); + } + } + }); + + // Async update + new HolderTask(position, holder, xAppInfo).executeOnExecutor(mExecutor, (Object) null); + + return convertView; + } + } + + // Helper methods + + private void setProgress(String text, int progress, int max) { + // Set up the progress bar + if (mProgressWidth == 0) { + final View vProgressEmpty = (View) findViewById(R.id.vProgressEmpty); + mProgressWidth = vProgressEmpty.getMeasuredWidth(); + } + // Display stuff + TextView tvState = (TextView) findViewById(R.id.tvState); + if (text != null) + tvState.setText(text); + if (max == 0) + max = 1; + mProgress = (int) ((float) mProgressWidth) * progress / max; + + View vProgressFull = (View) findViewById(R.id.vProgressFull); + vProgressFull.getLayoutParams().width = mProgress; + } + + private int getSelectedCategory(final int userId) { + int pos = 0; + String restrictionName = PrivacyManager.getSetting(userId, PrivacyManager.cSettingSelectedCategory, null); + if (restrictionName != null) + for (String restriction : PrivacyManager.getRestrictions(this).values()) { + pos++; + if (restrictionName.equals(restriction)) + break; + } + return pos; + } + + private void checkLicense() { + if (!Util.isProEnabled() && Util.hasProLicense(this) == null) + if (Util.isProEnablerInstalled(this)) + try { + int uid = getPackageManager().getPackageInfo("biz.bokhorst.xprivacy.pro", 0).applicationInfo.uid; + PrivacyManager.deleteRestrictions(uid, null, true); + Util.log(null, Log.INFO, "Licensing: check"); + startActivityForResult(new Intent("biz.bokhorst.xprivacy.pro.CHECK"), ACTIVITY_LICENSE); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivitySettings.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivitySettings.java new file mode 100644 index 0000000..ef6feaa --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivitySettings.java @@ -0,0 +1,782 @@ +package biz.bokhorst.xprivacy; + +import java.io.File; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.location.Address; +import android.location.Geocoder; +import android.os.Bundle; +import android.os.Environment; +import android.os.Process; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +public class ActivitySettings extends ActivityBase implements OnCheckedChangeListener, OnClickListener { + private int userId; + private int uid; + private boolean isApp; + private boolean odSystem; + private boolean expert; + + private CheckBox cbNotify; + private CheckBox cbOnDemand; + private CheckBox cbBlacklist; + private CheckBox cbUsage; + private CheckBox cbParameters; + private CheckBox cbValues; + private CheckBox cbLog; + private CheckBox cbSystem; + private CheckBox cbExperimental; + private CheckBox cbHttps; + private CheckBox cbAOSP; + private EditText etConfidence; + private EditText etQuirks; + private Button btnFlush; + private Button btnClearDb; + private CheckBox cbRandom; + private EditText etSerial; + private EditText etLat; + private EditText etLon; + private EditText etAlt; + private EditText etSearch; + private EditText etMac; + private EditText etIP; + private EditText etImei; + private EditText etPhone; + private EditText etId; + private EditText etGsfId; + private EditText etAdId; + private EditText etMcc; + private EditText etMnc; + private EditText etCountry; + private EditText etOperator; + private EditText etIccId; + private EditText etCid; + private EditText etLac; + private EditText etSubscriber; + private EditText etSSID; + private EditText etUa; + private CheckBox cbSerial; + private CheckBox cbLat; + private CheckBox cbLon; + private CheckBox cbAlt; + private CheckBox cbMac; + private CheckBox cbImei; + private CheckBox cbPhone; + private CheckBox cbId; + private CheckBox cbGsfId; + private CheckBox cbAdId; + private CheckBox cbCountry; + private CheckBox cbSubscriber; + private CheckBox cbSSID; + + public static final String ACTION_SETTINGS = "biz.bokhorst.xprivacy.action.SETTINGS"; + public static final String cUid = "Uid"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.settings); + setSupportActionBar((Toolbar) findViewById(R.id.widgetToolbar)); + setTitle(R.string.menu_settings); + + userId = Util.getUserId(Process.myUid()); + + final Bundle extras = getIntent().getExtras(); + if (extras != null && extras.containsKey(cUid)) + uid = extras.getInt(cUid); + else + uid = userId; + + // Reference controls + TextView tvInfo = (TextView) findViewById(R.id.tvInfo); + + cbNotify = (CheckBox) findViewById(R.id.cbNotify); + cbOnDemand = (CheckBox) findViewById(R.id.cbOnDemand); + cbBlacklist = (CheckBox) findViewById(R.id.cbBlacklist); + cbUsage = (CheckBox) findViewById(R.id.cbUsage); + cbParameters = (CheckBox) findViewById(R.id.cbParameters); + cbValues = (CheckBox) findViewById(R.id.cbValues); + cbLog = (CheckBox) findViewById(R.id.cbLog); + + CheckBox cbExpert = (CheckBox) findViewById(R.id.cbExpert); + cbSystem = (CheckBox) findViewById(R.id.cbSystem); + cbExperimental = (CheckBox) findViewById(R.id.cbExperimental); + cbHttps = (CheckBox) findViewById(R.id.cbHttps); + cbAOSP = (CheckBox) findViewById(R.id.cbAOSP); + LinearLayout llConfidence = (LinearLayout) findViewById(R.id.llConfidence); + etConfidence = (EditText) findViewById(R.id.etConfidence); + etQuirks = (EditText) findViewById(R.id.etQuirks); + btnFlush = (Button) findViewById(R.id.btnFlush); + btnClearDb = (Button) findViewById(R.id.btnClearDb); + + cbRandom = (CheckBox) findViewById(R.id.cbRandom); + Button btnRandom = (Button) findViewById(R.id.btnRandom); + Button btnClear = (Button) findViewById(R.id.btnClear); + + etSerial = (EditText) findViewById(R.id.etSerial); + etLat = (EditText) findViewById(R.id.etLat); + etLon = (EditText) findViewById(R.id.etLon); + etAlt = (EditText) findViewById(R.id.etAlt); + etSearch = (EditText) findViewById(R.id.etSearch); + Button btnSearch = (Button) findViewById(R.id.btnSearch); + etMac = (EditText) findViewById(R.id.etMac); + etIP = (EditText) findViewById(R.id.etIP); + etImei = (EditText) findViewById(R.id.etImei); + etPhone = (EditText) findViewById(R.id.etPhone); + etId = (EditText) findViewById(R.id.etId); + etGsfId = (EditText) findViewById(R.id.etGsfId); + etAdId = (EditText) findViewById(R.id.etAdId); + etMcc = (EditText) findViewById(R.id.etMcc); + etMnc = (EditText) findViewById(R.id.etMnc); + etCountry = (EditText) findViewById(R.id.etCountry); + etOperator = (EditText) findViewById(R.id.etOperator); + etIccId = (EditText) findViewById(R.id.etIccId); + etCid = (EditText) findViewById(R.id.etCid); + etLac = (EditText) findViewById(R.id.etLac); + etSubscriber = (EditText) findViewById(R.id.etSubscriber); + etSSID = (EditText) findViewById(R.id.etSSID); + etUa = (EditText) findViewById(R.id.etUa); + + cbSerial = (CheckBox) findViewById(R.id.cbSerial); + cbLat = (CheckBox) findViewById(R.id.cbLat); + cbLon = (CheckBox) findViewById(R.id.cbLon); + cbAlt = (CheckBox) findViewById(R.id.cbAlt); + cbMac = (CheckBox) findViewById(R.id.cbMac); + cbImei = (CheckBox) findViewById(R.id.cbImei); + cbPhone = (CheckBox) findViewById(R.id.cbPhone); + cbId = (CheckBox) findViewById(R.id.cbId); + cbGsfId = (CheckBox) findViewById(R.id.cbGsfId); + cbAdId = (CheckBox) findViewById(R.id.cbAdId); + cbCountry = (CheckBox) findViewById(R.id.cbCountry); + cbSubscriber = (CheckBox) findViewById(R.id.cbSubscriber); + cbSSID = (CheckBox) findViewById(R.id.cbSSID); + + // Listen for changes + cbParameters.setOnCheckedChangeListener(this); + cbValues.setOnCheckedChangeListener(this); + cbExpert.setOnCheckedChangeListener(this); + cbSerial.setOnCheckedChangeListener(this); + cbLat.setOnCheckedChangeListener(this); + cbLon.setOnCheckedChangeListener(this); + cbAlt.setOnCheckedChangeListener(this); + cbMac.setOnCheckedChangeListener(this); + cbImei.setOnCheckedChangeListener(this); + cbPhone.setOnCheckedChangeListener(this); + cbId.setOnCheckedChangeListener(this); + cbGsfId.setOnCheckedChangeListener(this); + cbAdId.setOnCheckedChangeListener(this); + cbCountry.setOnCheckedChangeListener(this); + cbSubscriber.setOnCheckedChangeListener(this); + cbSSID.setOnCheckedChangeListener(this); + + // Get current values + boolean usage = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingUsage, true); + boolean parameters = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingParameters, false); + boolean values = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingValues, false); + boolean log = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingLog, false); + + boolean components = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingSystem, false); + boolean experimental = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingExperimental, false); + boolean https = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingHttps, true); + boolean aosp = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingAOSPMode, false); + String confidence = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingConfidence, ""); + + // Get quirks + boolean freeze = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingFreeze, false); + boolean resolve = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingResolve, false); + boolean noresolve = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingNoResolve, false); + boolean permman = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingPermMan, false); + boolean iwall = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingIntentWall, false); + boolean safemode = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingSafeMode, false); + boolean test = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingTestVersions, false); + boolean updates = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingUpdates, false); + boolean odsystem = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingOnDemandSystem, false); + boolean wnomod = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingWhitelistNoModify, false); + boolean nousage = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingNoUsageData, false); + List listQuirks = new ArrayList(); + if (freeze) + listQuirks.add("freeze"); + if (resolve) + listQuirks.add("resolve"); + if (noresolve) + listQuirks.add("noresolve"); + if (permman) + listQuirks.add("permman"); + if (iwall) + listQuirks.add("iwall"); + if (safemode) + listQuirks.add("safemode"); + if (test) + listQuirks.add("test"); + if (updates) + listQuirks.add("updates"); + if (odsystem) + listQuirks.add("odsystem"); + if (wnomod) + listQuirks.add("wnomod"); + if (nousage) + listQuirks.add("nousage"); + Collections.sort(listQuirks); + String quirks = TextUtils.join(",", listQuirks.toArray()); + + expert = (components || experimental || !https || aosp || !"".equals(confidence) || listQuirks.size() > 0); + + // Application specific + boolean notify = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingNotify, true); + boolean ondemand = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingOnDemand, uid == userId); + boolean blacklist = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingBlacklist, false); + boolean enabled = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingRestricted, true); + + // Common + boolean random = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingRandom, false); + String serial = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingSerial, ""); + String lat = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingLatitude, ""); + String lon = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingLongitude, ""); + String alt = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingAltitude, ""); + String mac = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingMac, ""); + String imei = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingImei, ""); + String phone = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingPhone, ""); + String id = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingId, ""); + String gsfid = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingGsfId, ""); + String adid = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingAdId, ""); + String country = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingCountry, ""); + String subscriber = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingSubscriber, ""); + String ssid = PrivacyManager.getSetting(-uid, PrivacyManager.cSettingSSID, ""); + + // Set current values + if (uid == userId) { + // Global settings + tvInfo.setVisibility(View.GONE); + cbUsage.setChecked(usage); + cbParameters.setChecked(parameters); + cbValues.setChecked(values); + if (userId == 0) + cbLog.setChecked(log); + else { + cbLog.setVisibility(View.GONE); + btnFlush.setVisibility(View.GONE); + btnClearDb.setVisibility(View.GONE); + } + cbExpert.setChecked(expert); + + if (PrivacyManager.cVersion3 + && (!Util.isSELinuxEnforced() || "true".equals(Util.getXOption("ignoreselinux")))) + cbAOSP.setVisibility(View.VISIBLE); + + if (expert) { + cbSystem.setChecked(components); + cbExperimental.setChecked(experimental); + cbHttps.setChecked(https); + cbAOSP.setChecked(aosp); + etConfidence.setText(confidence); + etQuirks.setText(quirks); + } else { + cbSystem.setEnabled(false); + cbExperimental.setEnabled(false); + cbHttps.setEnabled(false); + cbHttps.setChecked(true); + cbAOSP.setEnabled(false); + cbAOSP.setChecked(false); + etConfidence.setEnabled(false); + etQuirks.setEnabled(false); + btnFlush.setEnabled(false); + btnClearDb.setEnabled(false); + } + } else { + // Display application names + ApplicationInfoEx appInfo = new ApplicationInfoEx(this, uid); + getSupportActionBar().setSubtitle(TextUtils.join(", ", appInfo.getApplicationName())); + + // Disable global settings + cbUsage.setVisibility(View.GONE); + cbParameters.setVisibility(View.GONE); + cbValues.setVisibility(View.GONE); + cbLog.setVisibility(View.GONE); + cbSystem.setVisibility(View.GONE); + cbExperimental.setVisibility(View.GONE); + cbHttps.setVisibility(View.GONE); + cbAOSP.setVisibility(View.GONE); + llConfidence.setVisibility(View.GONE); + btnFlush.setVisibility(View.GONE); + btnClearDb.setVisibility(View.GONE); + + cbExpert.setChecked(expert); + if (expert) + etQuirks.setText(quirks); + else + etQuirks.setEnabled(false); + } + + boolean gnotify = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingNotify, true); + if (uid == userId || gnotify) + cbNotify.setChecked(notify); + else + cbNotify.setVisibility(View.GONE); + + isApp = PrivacyManager.isApplication(uid); + odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, false); + boolean gondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + if (uid == userId || ((isApp || odSystem) && gondemand)) { + cbOnDemand.setChecked(ondemand); + cbOnDemand.setEnabled(enabled); + } else + cbOnDemand.setVisibility(View.GONE); + + String blFileName = Environment.getExternalStorageDirectory().getPath() + "/.xprivacy/blacklist"; + if (uid == userId || !new File(blFileName).exists()) + cbBlacklist.setVisibility(View.GONE); + else + cbBlacklist.setChecked(blacklist); + + // Common + cbRandom.setChecked(random); + + // Set randomize on access check boxes + cbSerial.setChecked(serial.equals(PrivacyManager.cValueRandom)); + cbLat.setChecked(lat.equals(PrivacyManager.cValueRandom)); + cbLon.setChecked(lon.equals(PrivacyManager.cValueRandom)); + cbAlt.setChecked(alt.equals(PrivacyManager.cValueRandom)); + cbMac.setChecked(mac.equals(PrivacyManager.cValueRandom)); + cbImei.setChecked(imei.equals(PrivacyManager.cValueRandom)); + cbPhone.setChecked(phone.equals(PrivacyManager.cValueRandom)); + cbId.setChecked(id.equals(PrivacyManager.cValueRandom)); + cbGsfId.setChecked(gsfid.equals(PrivacyManager.cValueRandom)); + cbAdId.setChecked(adid.equals(PrivacyManager.cValueRandom)); + cbCountry.setChecked(country.equals(PrivacyManager.cValueRandom)); + cbSubscriber.setChecked(subscriber.equals(PrivacyManager.cValueRandom)); + cbSSID.setChecked(ssid.equals(PrivacyManager.cValueRandom)); + + // Set fake values + etSerial.setText(cbSerial.isChecked() ? "" : serial); + etLat.setText(cbLat.isChecked() ? "" : lat); + etLon.setText(cbLon.isChecked() ? "" : lon); + etAlt.setText(cbAlt.isChecked() ? "" : alt); + etMac.setText(cbMac.isChecked() ? "" : mac); + etImei.setText(cbImei.isChecked() ? "" : imei); + etPhone.setText(cbPhone.isChecked() ? "" : phone); + etId.setText(cbId.isChecked() ? "" : id); + etGsfId.setText(cbGsfId.isChecked() ? "" : gsfid); + etAdId.setText(cbAdId.isChecked() ? "" : adid); + etCountry.setText(cbCountry.isChecked() ? "" : country); + etSubscriber.setText(cbSubscriber.isChecked() ? "" : subscriber); + etSSID.setText(cbSSID.isChecked() ? "" : ssid); + + etSerial.setEnabled(!cbSerial.isChecked()); + etLat.setEnabled(!cbLat.isChecked()); + etLon.setEnabled(!cbLon.isChecked()); + etAlt.setEnabled(!cbAlt.isChecked()); + + etSearch.setEnabled(Geocoder.isPresent()); + btnSearch.setEnabled(Geocoder.isPresent()); + + etMac.setEnabled(!cbMac.isChecked()); + etImei.setEnabled(!cbImei.isChecked()); + etPhone.setEnabled(!cbPhone.isChecked()); + etId.setEnabled(!cbId.isChecked()); + etGsfId.setEnabled(!cbGsfId.isChecked()); + etAdId.setEnabled(!cbAdId.isChecked()); + etCountry.setEnabled(!cbCountry.isChecked()); + etSubscriber.setEnabled(!cbSubscriber.isChecked()); + etSSID.setEnabled(!cbSSID.isChecked()); + + etIP.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingIP, "")); + etMcc.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingMcc, "")); + etMnc.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingMnc, "")); + etOperator.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingOperator, "")); + etIccId.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingIccId, "")); + etCid.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingCid, "")); + etLac.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingLac, "")); + etUa.setText(PrivacyManager.getSetting(-uid, PrivacyManager.cSettingUa, "")); + + btnFlush.setOnClickListener(this); + btnClearDb.setOnClickListener(this); + btnRandom.setOnClickListener(this); + btnClear.setOnClickListener(this); + btnSearch.setOnClickListener(this); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + if (inflater != null && PrivacyService.checkClient()) { + inflater.inflate(R.menu.settings, menu); + return true; + } else + return false; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_cancel: + finish(); + return true; + case R.id.menu_save: + optionSave(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + switch (buttonView.getId()) { + case R.id.cbParameters: + case R.id.cbValues: + if (isChecked && Util.hasProLicense(this) == null) { + buttonView.setChecked(false); + Util.viewUri(this, ActivityMain.cProUri); + } + break; + case R.id.cbExpert: + cbSystem.setEnabled(isChecked); + cbExperimental.setEnabled(isChecked); + cbHttps.setEnabled(isChecked); + cbAOSP.setEnabled(isChecked); + etConfidence.setEnabled(isChecked); + etQuirks.setEnabled(isChecked); + btnFlush.setEnabled(isChecked); + btnClearDb.setEnabled(isChecked); + if (isChecked) { + if (!expert) + Toast.makeText(this, getString(R.string.msg_expert), Toast.LENGTH_LONG).show(); + } else { + cbSystem.setChecked(false); + cbExperimental.setChecked(false); + cbHttps.setChecked(true); + cbAOSP.setChecked(false); + etConfidence.setText(""); + etQuirks.setText(""); + } + break; + case R.id.cbSerial: + etSerial.setEnabled(!isChecked); + break; + case R.id.cbLat: + etLat.setEnabled(!isChecked); + break; + case R.id.cbLon: + etLon.setEnabled(!isChecked); + break; + case R.id.cbAlt: + etAlt.setEnabled(!isChecked); + break; + case R.id.cbMac: + etMac.setEnabled(!isChecked); + break; + case R.id.cbImei: + etImei.setEnabled(!isChecked); + break; + case R.id.cbPhone: + etPhone.setEnabled(!isChecked); + break; + case R.id.cbId: + etId.setEnabled(!isChecked); + break; + case R.id.cbGsfId: + etGsfId.setEnabled(!isChecked); + break; + case R.id.cbAdId: + etAdId.setEnabled(!isChecked); + break; + case R.id.cbCountry: + etCountry.setEnabled(!isChecked); + break; + case R.id.cbSubscriber: + etSubscriber.setEnabled(!isChecked); + break; + case R.id.cbSSID: + etSSID.setEnabled(!isChecked); + break; + } + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.btnFlush: + flush(); + break; + case R.id.btnClearDb: + clearDB(); + break; + case R.id.btnRandom: + randomize(); + break; + case R.id.btnClear: + clear(); + break; + case R.id.btnSearch: + search(); + break; + } + } + + private void clearDB() { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivitySettings.this); + alertDialogBuilder.setTitle(R.string.menu_clear_db); + alertDialogBuilder.setMessage(R.string.msg_sure); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + PrivacyManager.clear(); + Toast.makeText(ActivitySettings.this, getString(R.string.msg_reboot), Toast.LENGTH_LONG).show(); + finish(); + + // Refresh main UI + Intent intent = new Intent(ActivitySettings.this, ActivityMain.class); + intent.putExtra(ActivityMain.cAction, ActivityMain.cActionRefresh); + startActivity(intent); + } + }); + alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + private void randomize() { + etSerial.setText(PrivacyManager.getRandomProp("SERIAL")); + etLat.setText(PrivacyManager.getRandomProp("LAT")); + etLon.setText(PrivacyManager.getRandomProp("LON")); + etAlt.setText(PrivacyManager.getRandomProp("ALT")); + etMac.setText(PrivacyManager.getRandomProp("MAC")); + etImei.setText(PrivacyManager.getRandomProp("IMEI")); + etPhone.setText(PrivacyManager.getRandomProp("PHONE")); + etId.setText(PrivacyManager.getRandomProp("ANDROID_ID")); + etGsfId.setText(PrivacyManager.getRandomProp("GSF_ID")); + etAdId.setText(PrivacyManager.getRandomProp("AdvertisingId")); + etCountry.setText(PrivacyManager.getRandomProp("ISO3166")); + etSubscriber.setText(PrivacyManager.getRandomProp("SubscriberId")); + etSSID.setText(PrivacyManager.getRandomProp("SSID")); + } + + private void clear() { + final EditText[] edits = new EditText[] { etSerial, etLat, etLon, etAlt, etMac, etIP, etImei, etPhone, etId, + etGsfId, etAdId, etMcc, etMnc, etCountry, etOperator, etIccId, etCid, etLac, etSubscriber, etSSID, etUa }; + final CheckBox[] boxes = new CheckBox[] { cbSerial, cbLat, cbLon, cbAlt, cbMac, cbImei, cbPhone, cbId, cbGsfId, + cbAdId, cbCountry, cbSubscriber, cbSSID }; + + for (EditText edit : edits) + edit.setText(""); + etSearch.setText(""); + + for (CheckBox box : boxes) + box.setChecked(false); + } + + private void search() { + try { + String search = etSearch.getText().toString(); + final List
listAddress = new Geocoder(ActivitySettings.this).getFromLocationName(search, 1); + if (listAddress.size() > 0) { + Address address = listAddress.get(0); + + // Get coordinates + if (address.hasLatitude()) { + cbLat.setChecked(false); + etLat.setText(Double.toString(address.getLatitude())); + } + if (address.hasLongitude()) { + cbLon.setChecked(false); + etLon.setText(Double.toString(address.getLongitude())); + } + + // Get address + StringBuilder sb = new StringBuilder(); + for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) { + if (i != 0) + sb.append(", "); + sb.append(address.getAddressLine(i)); + } + etSearch.setText(sb.toString()); + } + } catch (Throwable ex) { + Toast.makeText(ActivitySettings.this, ex.getMessage(), Toast.LENGTH_LONG).show(); + } + } + + private void flush() { + Intent flushIntent = new Intent(UpdateService.cFlush); + flushIntent.setPackage(getPackageName()); + startService(flushIntent); + Toast.makeText(ActivitySettings.this, getString(R.string.msg_done), Toast.LENGTH_LONG).show(); + } + + @SuppressLint("DefaultLocale") + private void optionSave() { + if (uid == userId) { + // Global settings + PrivacyManager.setSetting(uid, PrivacyManager.cSettingUsage, Boolean.toString(cbUsage.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingParameters, + Boolean.toString(cbParameters.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingValues, Boolean.toString(cbValues.isChecked())); + if (userId == 0) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLog, Boolean.toString(cbLog.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingSystem, Boolean.toString(cbSystem.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingExperimental, + Boolean.toString(cbExperimental.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingHttps, Boolean.toString(cbHttps.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingAOSPMode, Boolean.toString(cbAOSP.isChecked())); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingConfidence, etConfidence.getText().toString()); + } + + // Quirks + List listQuirks = Arrays + .asList(etQuirks.getText().toString().toLowerCase().replace(" ", "").split(",")); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingFreeze, Boolean.toString(listQuirks.contains("freeze"))); + PrivacyManager + .setSetting(uid, PrivacyManager.cSettingResolve, Boolean.toString(listQuirks.contains("resolve"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingNoResolve, + Boolean.toString(listQuirks.contains("noresolve"))); + PrivacyManager + .setSetting(uid, PrivacyManager.cSettingPermMan, Boolean.toString(listQuirks.contains("permman"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingIntentWall, + Boolean.toString(listQuirks.contains("iwall"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingSafeMode, + Boolean.toString(listQuirks.contains("safemode"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingTestVersions, + Boolean.toString(listQuirks.contains("test"))); + PrivacyManager + .setSetting(uid, PrivacyManager.cSettingUpdates, Boolean.toString(listQuirks.contains("updates"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOnDemandSystem, + Boolean.toString(listQuirks.contains("odsystem"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingWhitelistNoModify, + Boolean.toString(listQuirks.contains("wnomod"))); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingNoUsageData, + Boolean.toString(listQuirks.contains("nousage"))); + + // Notifications + PrivacyManager.setSetting(uid, PrivacyManager.cSettingNotify, Boolean.toString(cbNotify.isChecked())); + + // On demand restricting + if (uid == userId || (isApp || odSystem)) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOnDemand, Boolean.toString(cbOnDemand.isChecked())); + + if (uid != userId) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingBlacklist, Boolean.toString(cbBlacklist.isChecked())); + + // Random at boot + PrivacyManager.setSetting(uid, PrivacyManager.cSettingRandom, cbRandom.isChecked() ? Boolean.toString(true) + : null); + + // Serial# + PrivacyManager.setSetting(uid, PrivacyManager.cSettingSerial, getValue(cbSerial, etSerial)); + + // Set latitude + if (cbLat.isChecked()) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLatitude, PrivacyManager.cValueRandom); + else + try { + float lat = Float.parseFloat(etLat.getText().toString().replace(',', '.')); + if (lat < -90 || lat > 90) + throw new InvalidParameterException(); + + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLatitude, Float.toString(lat)); + } catch (Throwable ignored) { + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLatitude, null); + } + + // Set longitude + if (cbLon.isChecked()) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLongitude, PrivacyManager.cValueRandom); + else + try { + float lon = Float.parseFloat(etLon.getText().toString().replace(',', '.')); + if (lon < -180 || lon > 180) + throw new InvalidParameterException(); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLongitude, Float.toString(lon)); + } catch (Throwable ignored) { + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLongitude, null); + } + + // Set altitude + if (cbAlt.isChecked()) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingAltitude, PrivacyManager.cValueRandom); + else + try { + float alt = Float.parseFloat(etAlt.getText().toString().replace(',', '.')); + if (alt < -10000 || alt > 10000) + throw new InvalidParameterException(); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingAltitude, Float.toString(alt)); + } catch (Throwable ignored) { + PrivacyManager.setSetting(uid, PrivacyManager.cSettingAltitude, null); + } + + // Check Gsf ID + try { + String value = etGsfId.getText().toString(); + if (!"".equals(value)) + Long.parseLong(value.toLowerCase(), 16); + } catch (NumberFormatException ignored) { + etGsfId.setText(""); + } + + // Other settings + PrivacyManager.setSetting(uid, PrivacyManager.cSettingMac, getValue(cbMac, etMac)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingIP, getValue(null, etIP)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingImei, getValue(cbImei, etImei)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingPhone, getValue(cbPhone, etPhone)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingId, getValue(cbId, etId)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingGsfId, getValue(cbGsfId, etGsfId)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingAdId, getValue(cbAdId, etAdId)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingMcc, getValue(null, etMcc)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingMnc, getValue(null, etMnc)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingCountry, getValue(cbCountry, etCountry)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOperator, getValue(null, etOperator)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingIccId, getValue(null, etIccId)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingCid, getValue(null, etCid)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingLac, getValue(null, etLac)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingSubscriber, getValue(cbSubscriber, etSubscriber)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingSSID, getValue(cbSSID, etSSID)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingUa, getValue(null, etUa)); + + finish(); + + // Refresh view + if (uid == userId) { + Intent intent = new Intent(ActivitySettings.this, ActivityMain.class); + startActivity(intent); + } else { + Intent intent = new Intent(ActivitySettings.this, ActivityApp.class); + intent.putExtra(ActivityApp.cUid, uid); + intent.putExtra(ActivityApp.cAction, ActivityApp.cActionRefresh); + startActivity(intent); + } + } + + private static String getValue(CheckBox check, EditText edit) { + if (check != null && check.isChecked()) + return PrivacyManager.cValueRandom; + String value = edit.getText().toString().trim(); + return ("".equals(value) ? null : value); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityShare.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityShare.java new file mode 100644 index 0000000..ba9ef89 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityShare.java @@ -0,0 +1,2132 @@ +package biz.bokhorst.xprivacy; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import javax.net.ssl.SSLException; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.HttpHostConnectException; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.json.JSONArray; +import org.json.JSONObject; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; +import org.xmlpull.v1.XmlSerializer; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.ProgressDialog; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.graphics.Color; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.PowerManager; +import android.os.Process; +import android.provider.ContactsContract; +import android.provider.Settings.Secure; +import android.support.v4.app.NotificationCompat; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.Log; +import android.util.Patterns; +import android.util.SparseArray; +import android.util.Xml; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.ScrollView; +import android.widget.Spinner; +import android.widget.RadioGroup.OnCheckedChangeListener; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.Toast; + +@SuppressLint("Wakelock") +public class ActivityShare extends ActivityBase { + private int mActionId; + private AppListAdapter mAppAdapter; + private SparseArray mAppsByUid; + private boolean mRunning = false; + private boolean mAbort = false; + private int mProgressCurrent; + private int mProgressWidth = 0; + private String mFileName = null; + private boolean mInteractive = false; + + private static final int STATE_WAITING = 0; + private static final int STATE_RUNNING = 1; + private static final int STATE_SUCCESS = 2; + private static final int STATE_FAILURE = 3; + + private static final int ACTIVITY_IMPORT_SELECT = 0; + + public static final String cUidList = "UidList"; + public static final String cRestriction = "Restriction"; + public static final String cInteractive = "Interactive"; + public static final String cChoice = "Choice"; + public static final String cFileName = "FileName"; + public static final String HTTP_BASE_URL = "http://crowd.xprivacy.eu/"; + public static final String HTTPS_BASE_URL = "https://crowd.xprivacy.eu/"; + + public static final int cSubmitLimit = 10; + public static final int cProtocolVersion = 4; + + public static final String ACTION_EXPORT = "biz.bokhorst.xprivacy.action.EXPORT"; + public static final String ACTION_IMPORT = "biz.bokhorst.xprivacy.action.IMPORT"; + public static final String ACTION_FETCH = "biz.bokhorst.xprivacy.action.FETCH"; + public static final String ACTION_SUBMIT = "biz.bokhorst.xprivacy.action.SUBMIT"; + public static final String ACTION_TOGGLE = "biz.bokhorst.xprivacy.action.TOGGLE"; + + public static final int CHOICE_CLEAR = 1; + public static final int CHOICE_TEMPLATE = 2; + + public static final int TIMEOUT_MILLISEC = 45000; + + private static ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), + new PriorityThreadFactory()); + + private static class PriorityThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.NORM_PRIORITY); + return t; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Check privacy service client + if (!PrivacyService.checkClient()) + return; + + // Get data + int userId = Util.getUserId(Process.myUid()); + final Bundle extras = getIntent().getExtras(); + final String action = getIntent().getAction(); + final int[] uids = (extras != null && extras.containsKey(cUidList) ? extras.getIntArray(cUidList) : new int[0]); + final String restrictionName = (extras != null ? extras.getString(cRestriction) : null); + int choice = (extras != null && extras.containsKey(cChoice) ? extras.getInt(cChoice) : -1); + if (action.equals(ACTION_EXPORT)) + mFileName = (extras != null && extras.containsKey(cFileName) ? extras.getString(cFileName) : null); + + // License check + if (action.equals(ACTION_IMPORT) || action.equals(ACTION_EXPORT)) { + if (!Util.isProEnabled() && Util.hasProLicense(this) == null) { + Util.viewUri(this, ActivityMain.cProUri); + finish(); + return; + } + } else if (action.equals(ACTION_FETCH) || (action.equals(ACTION_TOGGLE) && uids.length > 1)) { + if (Util.hasProLicense(this) == null) { + Util.viewUri(this, ActivityMain.cProUri); + finish(); + return; + } + } + + // Registration check + if (action.equals(ACTION_SUBMIT) && !registerDevice(this)) { + finish(); + return; + } + + // Check whether we need a user interface + if (extras != null && extras.containsKey(cInteractive) && extras.getBoolean(cInteractive, false)) + mInteractive = true; + + // Set layout + setContentView(R.layout.sharelist); + setSupportActionBar((Toolbar) findViewById(R.id.widgetToolbar)); + + // Reference controls + final TextView tvDescription = (TextView) findViewById(R.id.tvDescription); + final ScrollView svToggle = (ScrollView) findViewById(R.id.svToggle); + final RadioGroup rgToggle = (RadioGroup) findViewById(R.id.rgToggle); + final Spinner spRestriction = (Spinner) findViewById(R.id.spRestriction); + RadioButton rbClear = (RadioButton) findViewById(R.id.rbClear); + RadioButton rbTemplateFull = (RadioButton) findViewById(R.id.rbTemplateFull); + RadioButton rbODEnable = (RadioButton) findViewById(R.id.rbEnableOndemand); + RadioButton rbODDisable = (RadioButton) findViewById(R.id.rbDisableOndemand); + final Spinner spTemplate = (Spinner) findViewById(R.id.spTemplate); + final CheckBox cbClear = (CheckBox) findViewById(R.id.cbClear); + final Button btnOk = (Button) findViewById(R.id.btnOk); + final Button btnCancel = (Button) findViewById(R.id.btnCancel); + + // Set title + if (action.equals(ACTION_TOGGLE)) { + mActionId = R.string.menu_toggle; + getSupportActionBar().setSubtitle(R.string.menu_toggle); + } else if (action.equals(ACTION_IMPORT)) { + mActionId = R.string.menu_import; + getSupportActionBar().setSubtitle(R.string.menu_import); + } else if (action.equals(ACTION_EXPORT)) { + mActionId = R.string.menu_export; + getSupportActionBar().setSubtitle(R.string.menu_export); + } else if (action.equals(ACTION_FETCH)) { + mActionId = R.string.menu_fetch; + getSupportActionBar().setSubtitle(R.string.menu_fetch); + } else if (action.equals(ACTION_SUBMIT)) { + mActionId = R.string.menu_submit; + getSupportActionBar().setSubtitle(R.string.menu_submit); + } else { + finish(); + return; + } + + // Get localized restriction name + List listRestrictionName = new ArrayList(PrivacyManager.getRestrictions(this).navigableKeySet()); + listRestrictionName.add(0, getString(R.string.menu_all)); + + // Build restriction adapter + SpinnerAdapter saRestriction = new SpinnerAdapter(this, android.R.layout.simple_spinner_item); + saRestriction.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + saRestriction.addAll(listRestrictionName); + + // Setup restriction spinner + int pos = 0; + if (restrictionName != null) + for (String restriction : PrivacyManager.getRestrictions(this).values()) { + pos++; + if (restrictionName.equals(restriction)) + break; + } + + spRestriction.setAdapter(saRestriction); + spRestriction.setSelection(pos); + + // Build template adapter + SpinnerAdapter spAdapter = new SpinnerAdapter(this, android.R.layout.simple_spinner_item); + spAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + String defaultName = PrivacyManager.getSetting(userId, Meta.cTypeTemplateName, "0", + getString(R.string.title_default)); + spAdapter.add(defaultName); + for (int i = 1; i <= 4; i++) { + String alternateName = PrivacyManager.getSetting(userId, Meta.cTypeTemplateName, Integer.toString(i), + getString(R.string.title_alternate) + " " + i); + spAdapter.add(alternateName); + } + spTemplate.setAdapter(spAdapter); + + // Build application list + AppListTask appListTask = new AppListTask(); + appListTask.executeOnExecutor(mExecutor, uids); + + // Import/export filename + if (action.equals(ACTION_EXPORT) || action.equals(ACTION_IMPORT)) { + // Check for availability of sharing intent + Intent file = new Intent(Intent.ACTION_GET_CONTENT); + file.setType("file/*"); + boolean hasIntent = Util.isIntentAvailable(ActivityShare.this, file); + + // Get file name + if (mFileName == null) + if (action.equals(ACTION_EXPORT)) { + String packageName = null; + if (uids.length == 1) + try { + ApplicationInfoEx appInfo = new ApplicationInfoEx(this, uids[0]); + packageName = appInfo.getPackageName().get(0); + } catch (Throwable ex) { + Util.bug(null, ex); + } + mFileName = getFileName(this, hasIntent, packageName); + } else + mFileName = (hasIntent ? null : getFileName(this, false, null)); + + if (mFileName == null) + fileChooser(); + else + showFileName(); + + if (action.equals(ACTION_IMPORT)) + cbClear.setVisibility(View.VISIBLE); + + } else if (action.equals(ACTION_FETCH)) { + tvDescription.setText(getBaseURL()); + cbClear.setVisibility(View.VISIBLE); + + } else if (action.equals(ACTION_TOGGLE)) { + tvDescription.setVisibility(View.GONE); + spRestriction.setVisibility(View.VISIBLE); + svToggle.setVisibility(View.VISIBLE); + + // Listen for radio button + rgToggle.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + btnOk.setEnabled(checkedId >= 0); + spRestriction.setVisibility(checkedId == R.id.rbEnableOndemand + || checkedId == R.id.rbDisableOndemand ? View.GONE : View.VISIBLE); + + spTemplate + .setVisibility(checkedId == R.id.rbTemplateCategory || checkedId == R.id.rbTemplateFull + || checkedId == R.id.rbTemplateMergeSet || checkedId == R.id.rbTemplateMergeReset ? View.VISIBLE + : View.GONE); + } + }); + + boolean ondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + rbODEnable.setVisibility(ondemand ? View.VISIBLE : View.GONE); + rbODDisable.setVisibility(ondemand ? View.VISIBLE : View.GONE); + + if (choice == CHOICE_CLEAR) + rbClear.setChecked(true); + else if (choice == CHOICE_TEMPLATE) + rbTemplateFull.setChecked(true); + + } else + tvDescription.setText(getBaseURL()); + + if (mInteractive) { + // Enable ok + // (showFileName does this for export/import) + if (action.equals(ACTION_SUBMIT) || action.equals(ACTION_FETCH)) + btnOk.setEnabled(true); + + // Listen for ok + btnOk.setOnClickListener(new Button.OnClickListener() { + @Override + public void onClick(View v) { + btnOk.setEnabled(false); + + // Toggle + if (action.equals(ACTION_TOGGLE)) { + mRunning = true; + for (int i = 0; i < rgToggle.getChildCount(); i++) + ((RadioButton) rgToggle.getChildAt(i)).setEnabled(false); + int pos = spRestriction.getSelectedItemPosition(); + String restrictionName = (pos == 0 ? null : (String) PrivacyManager + .getRestrictions(ActivityShare.this).values().toArray()[pos - 1]); + new ToggleTask().executeOnExecutor(mExecutor, restrictionName); + + // Import + } else if (action.equals(ACTION_IMPORT)) { + mRunning = true; + cbClear.setEnabled(false); + new ImportTask().executeOnExecutor(mExecutor, new File(mFileName), cbClear.isChecked()); + } + + // Export + else if (action.equals(ACTION_EXPORT)) { + mRunning = true; + new ExportTask().executeOnExecutor(mExecutor, new File(mFileName)); + + // Fetch + } else if (action.equals(ACTION_FETCH)) { + if (uids.length > 0) { + mRunning = true; + cbClear.setEnabled(false); + new FetchTask().executeOnExecutor(mExecutor, cbClear.isChecked()); + } + } + + // Submit + else if (action.equals(ACTION_SUBMIT)) { + if (uids.length > 0) { + if (uids.length <= cSubmitLimit) { + mRunning = true; + new SubmitTask().executeOnExecutor(mExecutor); + } else { + String message = getString(R.string.msg_limit, cSubmitLimit + 1); + Toast.makeText(ActivityShare.this, message, Toast.LENGTH_LONG).show(); + btnOk.setEnabled(false); + } + } + } + } + }); + + } else + btnOk.setEnabled(false); + + // Listen for cancel + btnCancel.setOnClickListener(new Button.OnClickListener() { + @Override + public void onClick(View v) { + if (mRunning) { + mAbort = true; + Toast.makeText(ActivityShare.this, getString(R.string.msg_abort), Toast.LENGTH_LONG).show(); + } else + finish(); + } + }); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent dataIntent) { + super.onActivityResult(requestCode, resultCode, dataIntent); + + // Import select + if (requestCode == ACTIVITY_IMPORT_SELECT) + if (resultCode == RESULT_CANCELED || dataIntent == null) + finish(); + else { + String fileName = dataIntent.getData().getPath(); + mFileName = fileName.replace("/document/primary:", Environment.getExternalStorageDirectory() + .getAbsolutePath() + File.separatorChar); + showFileName(); + } + } + + // State management + + public void setState(int uid, int state, String message) { + final AppState app = mAppsByUid.get(uid); + app.message = message; + app.state = state; + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mAppAdapter != null) { + mAppAdapter.notifyDataSetChanged(); + + int position = mAppAdapter.getPosition(app); + if (position >= 0) { + ListView lvShare = (ListView) findViewById(R.id.lvShare); + lvShare.smoothScrollToPosition(position); + } + } + } + }); + } + + public void setState(int uid, int state) { + AppState app = mAppsByUid.get(uid); + app.state = state; + } + + public void setMessage(int uid, String message) { + AppState app = mAppsByUid.get(uid); + app.message = message; + } + + // App info and share state + + private class AppState implements Comparable { + public int state = STATE_WAITING; + public String message = null; + public ApplicationInfoEx appInfo; + + public AppState(int uid) { + appInfo = new ApplicationInfoEx(ActivityShare.this, uid); + } + + @Override + public int compareTo(AppState other) { + return this.appInfo.compareTo(other.appInfo); + } + } + + // Adapters + + private class SpinnerAdapter extends ArrayAdapter { + public SpinnerAdapter(Context context, int textViewResourceId) { + super(context, textViewResourceId); + } + } + + private class AppListAdapter extends ArrayAdapter { + private LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + public AppListAdapter(Context context, int resource, List objects) { + super(context, resource, objects); + } + + public List getListUid() { + List uids = new ArrayList(); + for (int i = 0; i < this.getCount(); i++) + uids.add(this.getItem(i).appInfo.getUid()); + return uids; + } + + public List getListAppInfo() { + List apps = new ArrayList(); + for (int i = 0; i < this.getCount(); i++) + apps.add(this.getItem(i).appInfo); + return apps; + } + + private class ViewHolder { + private View row; + private int position; + public ImageView imgIcon; + public ImageView imgInfo; + public TextView tvName; + public ImageView imgResult; + public ProgressBar pbRunning; + public TextView tvMessage; + + public ViewHolder(View theRow, int thePosition) { + row = theRow; + position = thePosition; + imgIcon = (ImageView) row.findViewById(R.id.imgIcon); + imgInfo = (ImageView) row.findViewById(R.id.imgInfo); + tvName = (TextView) row.findViewById(R.id.tvApp); + imgResult = (ImageView) row.findViewById(R.id.imgResult); + pbRunning = (ProgressBar) row.findViewById(R.id.pbRunning); + tvMessage = (TextView) row.findViewById(R.id.tvMessage); + } + } + + @Override + @SuppressLint("InflateParams") + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.shareentry, null); + holder = new ViewHolder(convertView, position); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + holder.position = position; + } + + // Get info + final AppState xApp = getItem(holder.position); + + // Set background color + if (xApp.appInfo.isSystem()) + holder.row.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_dangerous))); + else + holder.row.setBackgroundColor(Color.TRANSPARENT); + + // Display icon + holder.imgIcon.setImageDrawable(xApp.appInfo.getIcon(ActivityShare.this)); + holder.imgIcon.setVisibility(View.VISIBLE); + + holder.imgInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Packages can be selected on the web site + Util.viewUri(ActivityShare.this, Uri.parse(String.format(getBaseURL() + "?package_name=%s", + xApp.appInfo.getPackageName().get(0)))); + } + }); + + // Set app name + holder.tvName.setText(xApp.appInfo.toString()); + + // Show app share state + if (TextUtils.isEmpty(xApp.message)) + holder.tvMessage.setVisibility(View.GONE); + else { + holder.tvMessage.setVisibility(View.VISIBLE); + holder.tvMessage.setText(xApp.message); + } + switch (xApp.state) { + case STATE_WAITING: + holder.imgResult.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.GONE); + break; + case STATE_RUNNING: + holder.imgResult.setVisibility(View.GONE); + holder.pbRunning.setVisibility(View.VISIBLE); + break; + case STATE_SUCCESS: + holder.imgResult.setBackgroundResource(R.drawable.btn_check_buttonless_on); + holder.imgResult.setVisibility(View.VISIBLE); + holder.pbRunning.setVisibility(View.GONE); + break; + case STATE_FAILURE: + holder.imgResult.setBackgroundResource(R.drawable.indicator_input_error); + holder.imgResult.setVisibility(View.VISIBLE); + holder.pbRunning.setVisibility(View.GONE); + break; + default: + Util.log(null, Log.ERROR, "Unknown state=" + xApp.state); + break; + } + + return convertView; + } + } + + // Tasks + + private class AppListTask extends AsyncTask> { + private ProgressDialog mProgressDialog; + + @Override + protected List doInBackground(int[]... params) { + int[] uids = params[0]; + List apps = new ArrayList(); + mAppsByUid = new SparseArray(); + + if (!mInteractive && mActionId == R.string.menu_export) { + // Build list of distinct uids for export + List listUid = new ArrayList(); + for (PackageInfo pInfo : getPackageManager().getInstalledPackages(0)) + if (!listUid.contains(pInfo.applicationInfo.uid)) + listUid.add(pInfo.applicationInfo.uid); + + // Convert to primitive array + uids = new int[listUid.size()]; + for (int i = 0; i < listUid.size(); i++) + uids[i] = listUid.get(i); + } + + mProgressDialog.setMax(uids.length); + for (int i = 0; i < uids.length; i++) { + mProgressDialog.setProgress(i); + AppState app = new AppState(uids[i]); + apps.add(app); + mAppsByUid.put(uids[i], app); + } + + Collections.sort(apps); + return apps; + } + + @SuppressWarnings("deprecation") + @Override + protected void onPreExecute() { + super.onPreExecute(); + + TypedArray ta = getTheme().obtainStyledAttributes(new int[] { R.attr.progress_horizontal }); + int progress_horizontal = ta.getResourceId(0, 0); + ta.recycle(); + + // Show progress dialog + mProgressDialog = new ProgressDialog(ActivityShare.this); + mProgressDialog.setMessage(getString(R.string.msg_loading)); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + mProgressDialog.setProgressDrawable(getResources().getDrawable(progress_horizontal)); + mProgressDialog.setProgressNumberFormat(null); + mProgressDialog.setCancelable(false); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressDialog.show(); + } + + @Override + protected void onPostExecute(List listApp) { + if (!ActivityShare.this.isFinishing()) { + // Display app list + mAppAdapter = new AppListAdapter(ActivityShare.this, R.layout.shareentry, listApp); + ListView lvShare = (ListView) findViewById(R.id.lvShare); + lvShare.setAdapter(mAppAdapter); + + // Dismiss progress dialog + if (mProgressDialog.isShowing()) + try { + mProgressDialog.dismiss(); + } catch (IllegalArgumentException ignored) { + } + + // Launch non-interactive export + if (!mInteractive && mActionId == R.string.menu_export) { + mRunning = true; + new ExportTask().executeOnExecutor(mExecutor, new File(mFileName)); + } + } + + super.onPostExecute(listApp); + } + } + + private class ToggleTask extends AsyncTask { + @Override + protected Throwable doInBackground(String... params) { + // Get wakelock + PowerManager pm = (PowerManager) ActivityShare.this.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XPrivacy.Toggle"); + wl.acquire(); + try { + // Get data + mProgressCurrent = 0; + List lstUid = mAppAdapter.getListUid(); + final String restrictionName = params[0]; + int actionId = ((RadioGroup) ActivityShare.this.findViewById(R.id.rgToggle)).getCheckedRadioButtonId(); + Spinner spTemplate = ((Spinner) ActivityShare.this.findViewById(R.id.spTemplate)); + String templateName = Meta.cTypeTemplate; + if (spTemplate.getSelectedItemPosition() > 0) + templateName = Meta.cTypeTemplate + spTemplate.getSelectedItemPosition(); + + for (Integer uid : lstUid) + try { + if (mAbort) + throw new AbortException(ActivityShare.this); + + // Update progess + publishProgress(++mProgressCurrent, lstUid.size() + 1); + setState(uid, STATE_RUNNING, null); + + List oldState = PrivacyManager.getRestartStates(uid, restrictionName); + + if (actionId == R.id.rbClear) { + PrivacyManager.deleteRestrictions(uid, restrictionName, (restrictionName == null)); + if (restrictionName == null) { + PrivacyManager.deleteUsage(uid); + PrivacyManager.deleteSettings(uid); + } + } + + else if (actionId == R.id.rbRestrict) { + PrivacyManager.setRestriction(uid, restrictionName, null, true, false); + PrivacyManager.updateState(uid); + } + + else if (actionId == R.id.rbTemplateCategory) + PrivacyManager.applyTemplate(uid, templateName, restrictionName, false, true, false); + + else if (actionId == R.id.rbTemplateFull) + PrivacyManager.applyTemplate(uid, templateName, restrictionName, true, true, false); + + else if (actionId == R.id.rbTemplateMergeSet) + PrivacyManager.applyTemplate(uid, templateName, restrictionName, true, false, false); + + else if (actionId == R.id.rbTemplateMergeReset) + PrivacyManager.applyTemplate(uid, templateName, restrictionName, true, false, true); + + else if (actionId == R.id.rbEnableOndemand) { + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOnDemand, Boolean.toString(true)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingNotify, Boolean.toString(false)); + + } else if (actionId == R.id.rbDisableOndemand) { + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOnDemand, Boolean.toString(false)); + PrivacyManager.setSetting(uid, PrivacyManager.cSettingNotify, Boolean.toString(true)); + + } else + Util.log(null, Log.ERROR, "Unknown action=" + actionId); + + List newState = PrivacyManager.getRestartStates(uid, restrictionName); + + setState(uid, STATE_SUCCESS, !newState.equals(oldState) ? getString(R.string.msg_restart) + : null); + } catch (Throwable ex) { + setState(uid, STATE_FAILURE, ex.getMessage()); + return ex; + } + } finally { + wl.release(); + } + + return null; + } + + @Override + protected void onProgressUpdate(Integer... values) { + blueStreakOfProgress(values[0], values[1]); + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Throwable result) { + if (!ActivityShare.this.isFinishing()) + done(result); + super.onPostExecute(result); + } + } + + private class ExportTask extends AsyncTask { + private File mFile; + + @Override + protected Throwable doInBackground(File... params) { + // Get wakelock + PowerManager pm = (PowerManager) ActivityShare.this.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XPrivacy.Export"); + wl.acquire(); + + mProgressCurrent = 0; + try { + mFile = params[0]; + + List listUid = mAppAdapter.getListUid(); + + Util.log(null, Log.INFO, "Exporting " + mFile); + String android_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); + + FileOutputStream fos = new FileOutputStream(mFile); + try { + // Start serialization + XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(fos, "UTF-8"); + serializer.startDocument(null, Boolean.valueOf(true)); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startTag(null, "XPrivacy"); + + // Process package map + for (PackageInfo pInfo : getPackageManager().getInstalledPackages(0)) + if (listUid.size() == 1 ? pInfo.applicationInfo.uid == listUid.get(0) : true) { + serializer.startTag(null, "PackageInfo"); + serializer.attribute(null, "Id", Integer.toString(pInfo.applicationInfo.uid)); + serializer.attribute(null, "Name", pInfo.packageName); + serializer.endTag(null, "PackageInfo"); + } + + // Process global settings + if (listUid.size() > 1) { + List listGlobalSetting = PrivacyManager.getSettingList(0, null); + for (PSetting setting : listGlobalSetting) { + // Serialize setting + serializer.startTag(null, "Setting"); + serializer.attribute(null, "Id", ""); + serializer.attribute(null, "Type", setting.type); + serializer.attribute(null, "Name", setting.name); + if (setting.value != null) + serializer.attribute(null, "Value", setting.value); + serializer.endTag(null, "Setting"); + } + } + + // Process application settings and restrictions + for (int uid : listUid) + try { + if (mAbort) + throw new AbortException(ActivityShare.this); + + publishProgress(++mProgressCurrent, listUid.size() + 1); + setState(uid, STATE_RUNNING, null); + + // Process application settings + List listAppSetting = PrivacyManager.getSettingList(uid, null); + for (PSetting setting : listAppSetting) { + // Bind accounts/contacts to same device + if (Meta.cTypeAccount.equals(setting.type) || Meta.cTypeContact.equals(setting.type)) + setting.name += "." + android_id; + + // Serialize setting + serializer.startTag(null, "Setting"); + serializer.attribute(null, "Id", Integer.toString(uid)); + serializer.attribute(null, "Type", setting.type); + serializer.attribute(null, "Name", setting.name); + serializer.attribute(null, "Value", setting.value); + serializer.endTag(null, "Setting"); + } + + // Process restrictions + for (String restrictionName : PrivacyManager.getRestrictions()) { + // Category + PRestriction crestricted = PrivacyManager.getRestrictionEx(uid, restrictionName, null); + serializer.startTag(null, "Restriction"); + serializer.attribute(null, "Id", Integer.toString(uid)); + serializer.attribute(null, "Name", restrictionName); + serializer.attribute(null, "Restricted", Boolean.toString(crestricted.restricted)); + serializer.attribute(null, "Asked", Boolean.toString(crestricted.asked)); + serializer.endTag(null, "Restriction"); + + // Methods + for (Hook md : PrivacyManager.getHooks(restrictionName, null)) { + PRestriction mrestricted = PrivacyManager.getRestrictionEx(uid, restrictionName, + md.getName()); + if ((crestricted.restricted && !mrestricted.restricted) + || (!crestricted.asked && mrestricted.asked) || md.isDangerous()) { + serializer.startTag(null, "Restriction"); + serializer.attribute(null, "Id", Integer.toString(uid)); + serializer.attribute(null, "Name", restrictionName); + serializer.attribute(null, "Method", md.getName()); + serializer.attribute(null, "Restricted", + Boolean.toString(mrestricted.restricted)); + serializer.attribute(null, "Asked", Boolean.toString(mrestricted.asked)); + serializer.endTag(null, "Restriction"); + } + } + } + + setState(uid, STATE_SUCCESS, null); + } catch (Throwable ex) { + setState(uid, STATE_FAILURE, ex.getMessage()); + throw ex; + } + // End serialization + serializer.endTag(null, "XPrivacy"); + serializer.endDocument(); + serializer.flush(); + } finally { + fos.close(); + } + + // Display message + Util.log(null, Log.INFO, "Exporting finished"); + return null; + } catch (Throwable ex) { + Util.bug(null, ex); + if (mFile.exists()) + mFile.delete(); + return ex; + } finally { + wl.release(); + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + blueStreakOfProgress(values[0], values[1]); + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Throwable result) { + if (!ActivityShare.this.isFinishing()) + if (mInteractive) { + done(result); + + // Share + if (result == null) { + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setType("text/xml"); + intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + mFileName)); + startActivity(Intent.createChooser(intent, + String.format(getString(R.string.msg_saved_to), mFileName))); + } + } else { + done(result); + finish(); + } + + super.onPostExecute(result); + } + } + + private class ImportTask extends AsyncTask { + @Override + protected Throwable doInBackground(Object... params) { + // Get wakelock + PowerManager pm = (PowerManager) ActivityShare.this.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XPrivacy.Import"); + wl.acquire(); + try { + // Parameters + File file = (File) params[0]; + boolean clear = (Boolean) params[1]; + List listUidSelected = mAppAdapter.getListUid(); + + // Progress + mProgressCurrent = 0; + final int max = listUidSelected.size() + 1; + Runnable progress = new Runnable() { + @Override + public void run() { + publishProgress(++mProgressCurrent, max); + } + }; + + // Parse XML + Util.log(null, Log.INFO, "Importing " + file); + FileInputStream fis = null; + Map>> mapPackage; + try { + fis = new FileInputStream(file); + XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); + ImportHandler importHandler = new ImportHandler(clear, listUidSelected, progress); + xmlReader.setContentHandler(importHandler); + xmlReader.parse(new InputSource(fis)); + mapPackage = importHandler.getPackageMap(); + + if (mAbort) + throw new AbortException(ActivityShare.this); + } finally { + if (fis != null) + fis.close(); + } + + // Progress + int progressMax = mapPackage.size(); + + // Process result (legacy) + for (String packageName : mapPackage.keySet()) { + if (mAbort) + throw new AbortException(ActivityShare.this); + + int uid = 0; + try { + publishProgress(++mProgressCurrent, progressMax + 1); + + // Get uid + uid = getPackageManager().getPackageInfo(packageName, 0).applicationInfo.uid; + + if (listUidSelected.contains(uid)) { + Util.log(null, Log.INFO, "Importing " + packageName); + setState(uid, STATE_RUNNING, null); + + // Reset existing restrictions + List oldState = PrivacyManager.getRestartStates(uid, null); + PrivacyManager.deleteRestrictions(uid, null, true); + + // Set imported restrictions + for (String restrictionName : mapPackage.get(packageName).keySet()) { + PrivacyManager.setRestriction(uid, restrictionName, null, true, false); + for (ImportHandler.MethodDescription md : mapPackage.get(packageName).get( + restrictionName)) + PrivacyManager.setRestriction(uid, restrictionName, md.getMethodName(), + md.isRestricted(), false); + } + PrivacyManager.updateState(uid); + List newState = PrivacyManager.getRestartStates(uid, null); + + setState(uid, STATE_SUCCESS, !newState.equals(oldState) ? getString(R.string.msg_restart) + : null); + } + } catch (NameNotFoundException ignored) { + } catch (Throwable ex) { + if (listUidSelected.contains(uid)) + setState(uid, STATE_FAILURE, ex.getMessage()); + Util.bug(null, ex); + } + } + + // Display message + Util.log(null, Log.INFO, "Importing finished"); + return null; + } catch (Throwable ex) { + return ex; + } finally { + wl.release(); + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + blueStreakOfProgress(values[0], values[1]); + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Throwable result) { + if (!ActivityShare.this.isFinishing()) + done(result); + super.onPostExecute(result); + } + } + + private class ImportHandler extends DefaultHandler { + private boolean mClear; + private List mListUidSelected; + private List mListUidSettings = new ArrayList(); + private List mListUidRestrictions = new ArrayList(); + + private int lastUid = -1; + private List mOldState = null; + + private SparseArray mMapId = new SparseArray(); + private Map mMapUid = new HashMap(); + private Map>> mMapPackage = new HashMap>>(); + + private Runnable mProgress; + private String android_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); + + public ImportHandler(boolean clear, List listUidSelected, Runnable progress) { + mClear = clear; + mListUidSelected = listUidSelected; + mProgress = progress; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) { + try { + if (qName.equals("XPrivacy")) { + // Root + + } else if (qName.equals("PackageInfo")) { + // Package info + int id = Integer.parseInt(attributes.getValue("Id")); + String name = attributes.getValue("Name"); + mMapId.put(id, name); + + } else if (qName.equals("Setting")) { + // Setting + String id = attributes.getValue("Id"); + String type = attributes.getValue("Type"); + String name = attributes.getValue("Name"); + String value = attributes.getValue("Value"); + + // Failsafe + if (name == null) + return; + + // Do not import version number + if (name.equals(PrivacyManager.cSettingVersion)) + return; + + // Decode legacy type + if (name.startsWith("Account.") || name.startsWith("Application.") || name.startsWith("Contact.") + || name.startsWith("Template.") || name.startsWith("Whitelist.")) { + name = name.replace("Whitelist.", ""); + int dot = name.indexOf('.'); + type = name.substring(0, dot); + name = name.substring(dot + 1); + } else if (type == null) + type = ""; + + // Import accounts/contacts only for same device + if (Meta.cTypeAccount.equals(type) || Meta.cTypeContact.equals(type)) + if (name.endsWith("." + android_id)) + name = name.replace("." + android_id, ""); + else + return; + + if (id == null) { + // Legacy + Util.log(null, Log.WARN, "Legacy " + name + "=" + value); + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, name, value); + + } else if ("".equals(id)) { + // Global setting + if (mListUidSelected.size() > 1) { + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, type, name, value); + } + + } else { + // Application setting + int iid = Integer.parseInt(id); + int uid = getUid(iid); + if (mListUidSelected.contains(uid)) { + // Check for abort + if (mAbort && !mListUidSettings.contains(uid)) { + setState(uid, STATE_FAILURE); + setMessage(uid, getString(R.string.msg_aborted)); + return; + } + + // Check for new uid + if (!mListUidSettings.contains(uid)) { + // Mark previous as success + if (lastUid > 0) { + boolean restart = !PrivacyManager.getRestartStates(lastUid, null).equals(mOldState); + setState(lastUid, STATE_SUCCESS, restart ? getString(R.string.msg_restart) : null); + } + + // Update state + lastUid = uid; + mListUidSettings.add(uid); + + // Update visible state + setState(uid, STATE_RUNNING, null); + + // Clear settings + if (mClear) + PrivacyManager.deleteSettings(uid); + } + + PrivacyManager.setSetting(uid, type, name, value); + } + } + + } else if (qName.equals("Package")) { + // Restriction (legacy) + String packageName = attributes.getValue("Name"); + String restrictionName = attributes.getValue("Restriction"); + String methodName = attributes.getValue("Method"); + boolean restricted = Boolean.parseBoolean(attributes.getValue("Restricted")); + Util.log(null, Log.WARN, "Legacy package=" + packageName + " " + restrictionName + "/" + methodName + + "=" + restricted); + + // Map package restriction + if (!mMapPackage.containsKey(packageName)) + mMapPackage.put(packageName, new HashMap>()); + if (!mMapPackage.get(packageName).containsKey(restrictionName)) + mMapPackage.get(packageName).put(restrictionName, new ArrayList()); + if (methodName != null) { + MethodDescription md = new MethodDescription(methodName, restricted); + mMapPackage.get(packageName).get(restrictionName).add(md); + } + + } else if (qName.equals("Restriction")) { + // Restriction (new style) + int id = Integer.parseInt(attributes.getValue("Id")); + String restrictionName = attributes.getValue("Name"); + String methodName = attributes.getValue("Method"); + boolean restricted = Boolean.parseBoolean(attributes.getValue("Restricted")); + boolean asked = Boolean.parseBoolean(attributes.getValue("Asked")); + + // Get uid + int uid = getUid(id); + if (mListUidSelected.contains(uid)) { + // Check for abort + if (mAbort && !mListUidRestrictions.contains(uid)) { + setState(uid, STATE_FAILURE); + setMessage(uid, getString(R.string.msg_aborted)); + return; + } + + // Check for new uid + if (!mListUidRestrictions.contains(uid)) { + // Mark previous as success + if (lastUid > 0) { + PrivacyManager.updateState(lastUid); + boolean restart = !PrivacyManager.getRestartStates(lastUid, null).equals(mOldState); + setState(lastUid, STATE_SUCCESS, restart ? getString(R.string.msg_restart) : null); + } + + // Update state + lastUid = uid; + mListUidRestrictions.add(uid); + mOldState = PrivacyManager.getRestartStates(uid, null); + + // Update visible state + setState(uid, STATE_RUNNING, null); + runOnUiThread(mProgress); + + // Clear restrictions + if (mClear) + PrivacyManager.deleteRestrictions(uid, null, false); + } + + // Set restriction + PrivacyManager.setRestriction(uid, restrictionName, methodName, restricted, asked); + } + } else + Util.log(null, Log.WARN, "Unknown element name=" + qName); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + @Override + public void endElement(String uri, String localName, String qName) { + if (qName.equals("XPrivacy")) { + if (lastUid > 0) { + PrivacyManager.updateState(lastUid); + boolean restart = !PrivacyManager.getRestartStates(lastUid, null).equals(mOldState); + setState(lastUid, STATE_SUCCESS, restart ? getString(R.string.msg_restart) : null); + } + + // Cleanup salt + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.removeLegacySalt(userId); + } + } + + private int getUid(int id) { + String packageName = mMapId.get(id); + if (packageName == null) { + Util.log(null, Log.WARN, "Unknown id=" + id); + return -1; + } else if (!mMapUid.containsKey(packageName)) + try { + int newuid = ActivityShare.this.getPackageManager().getPackageInfo(packageName, 0).applicationInfo.uid; + mMapUid.put(packageName, newuid); + } catch (NameNotFoundException ex) { + // Do not lookup again + mMapUid.put(packageName, -1); + Util.log(null, Log.WARN, "Unknown package name=" + packageName); + } + return (mMapUid.containsKey(packageName) ? mMapUid.get(packageName) : -1); + } + + public Map>> getPackageMap() { + return mMapPackage; + } + + public class MethodDescription { + private String mMethodName; + private boolean mRestricted; + + public MethodDescription(String methodName, boolean restricted) { + mMethodName = methodName; + mRestricted = restricted; + } + + public String getMethodName() { + return mMethodName; + } + + public boolean isRestricted() { + return mRestricted; + } + } + } + + private class FetchTask extends AsyncTask { + @Override + @SuppressLint("DefaultLocale") + protected Throwable doInBackground(Boolean... params) { + // Get wakelock + PowerManager pm = (PowerManager) ActivityShare.this.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XPrivacy.Fetch"); + wl.acquire(); + try { + // Get data + boolean clear = params[0]; + List lstApp = mAppAdapter.getListAppInfo(); + + int userId = Util.getUserId(Process.myUid()); + String[] license = Util.getProLicenseUnchecked(); + String android_id = Secure.getString(ActivityShare.this.getContentResolver(), Secure.ANDROID_ID); + PackageInfo xInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + String confidence = PrivacyManager.getSetting(userId, PrivacyManager.cSettingConfidence, ""); + + // Initialize progress + mProgressCurrent = 0; + + // Process applications + for (ApplicationInfoEx appInfo : lstApp) + try { + publishProgress(++mProgressCurrent, lstApp.size() + 1); + + if (mAbort) + throw new AbortException(ActivityShare.this); + + setState(appInfo.getUid(), STATE_RUNNING, ActivityShare.this.getString(R.string.menu_fetch)); + + JSONArray appName = new JSONArray(); + for (String name : appInfo.getApplicationName()) + appName.put(name); + + JSONArray pkgName = new JSONArray(); + for (String name : appInfo.getPackageName()) + pkgName.put(name); + + JSONArray pkgVersion = new JSONArray(); + for (String version : appInfo.getPackageVersionName(ActivityShare.this)) + pkgVersion.put(version); + + // Encode package + JSONObject jRoot = new JSONObject(); + jRoot.put("protocol_version", cProtocolVersion); + jRoot.put("android_id", Util.md5(android_id).toLowerCase()); + jRoot.put("android_sdk", Build.VERSION.SDK_INT); + jRoot.put("xprivacy_version", xInfo.versionCode); + jRoot.put("application_name", appName); + jRoot.put("package_name", pkgName); + jRoot.put("package_version", pkgVersion); + jRoot.put("email", license[1]); + jRoot.put("signature", license[2]); + jRoot.put("confidence", confidence); + + // Fetch + HttpParams httpParams = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC); + HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC); + HttpClient httpclient = new DefaultHttpClient(httpParams); + + HttpPost httpost = new HttpPost(getBaseURL() + "?format=json&action=fetch"); + httpost.setEntity(new ByteArrayEntity(jRoot.toString().getBytes("UTF-8"))); + httpost.setHeader("Accept", "application/json"); + httpost.setHeader("Content-type", "application/json"); + HttpResponse response = httpclient.execute(httpost); + StatusLine statusLine = response.getStatusLine(); + + if (mAbort) + throw new AbortException(ActivityShare.this); + + setState(appInfo.getUid(), STATE_RUNNING, ActivityShare.this.getString(R.string.msg_applying)); + + if (statusLine.getStatusCode() == HttpStatus.SC_OK) { + // Succeeded + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.getEntity().writeTo(out); + out.close(); + + // Deserialize + JSONObject status = new JSONObject(out.toString("UTF-8")); + if (status.getBoolean("ok")) { + JSONArray settings = status.getJSONArray("settings"); + // Delete existing restrictions + List oldState = PrivacyManager.getRestartStates(appInfo.getUid(), null); + + // Clear existing restriction + if (clear) + PrivacyManager.deleteRestrictions(appInfo.getUid(), null, true); + + // Set fetched restrictions + List listRestriction = new ArrayList(); + for (int i = 0; i < settings.length(); i++) { + JSONObject entry = settings.getJSONObject(i); + String restrictionName = entry.getString("restriction"); + String methodName = entry.has("method") ? entry.getString("method") : null; + int voted_restricted = entry.getInt("restricted"); + int voted_not_restricted = entry.getInt("not_restricted"); + boolean restricted = (voted_restricted > voted_not_restricted); + if (clear || restricted) + listRestriction.add(new PRestriction(appInfo.getUid(), restrictionName, + methodName, restricted)); + } + PrivacyManager.setRestrictionList(listRestriction); + List newState = PrivacyManager.getRestartStates(appInfo.getUid(), null); + + // Mark as new/changed + PrivacyManager.setSetting(appInfo.getUid(), PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_ATTENTION)); + + // Change app modification time + PrivacyManager.setSetting(appInfo.getUid(), PrivacyManager.cSettingModifyTime, + Long.toString(System.currentTimeMillis())); + + setState(appInfo.getUid(), STATE_SUCCESS, + !newState.equals(oldState) ? getString(R.string.msg_restart) : null); + } else { + int errno = status.getInt("errno"); + String message = status.getString("error"); + ServerException ex = new ServerException(ActivityShare.this, errno, message); + setState(appInfo.getUid(), STATE_FAILURE, ex.getMessage()); + } + } else { + // Failed + response.getEntity().getContent().close(); + throw new IOException(statusLine.getReasonPhrase()); + } + } catch (Throwable ex) { + setState(appInfo.getUid(), STATE_FAILURE, ex.getMessage()); + throw ex; + } + return null; + } catch (ConnectTimeoutException ex) { + return ex; + } catch (HttpHostConnectException ex) { + return ex; + } catch (SocketTimeoutException ex) { + return ex; + } catch (SSLException ex) { + return ex; + } catch (UnknownHostException ex) { + return ex; + } catch (IOException ex) { + return ex; + } catch (Throwable ex) { + Util.bug(null, ex); + return ex; + } finally { + wl.release(); + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + blueStreakOfProgress(values[0], values[1]); + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Throwable result) { + if (!ActivityShare.this.isFinishing()) + done(result); + super.onPostExecute(result); + } + } + + @SuppressLint("DefaultLocale") + private class SubmitTask extends AsyncTask { + @Override + protected Throwable doInBackground(Object... params) { + // Get wakelock + PowerManager pm = (PowerManager) ActivityShare.this.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XPrivacy.Submit"); + wl.acquire(); + try { + // Get data + List lstApp = mAppAdapter.getListAppInfo(); + + // Initialize progress + mProgressCurrent = 0; + + for (ApplicationInfoEx appInfo : lstApp) + try { + if (mAbort) + throw new AbortException(ActivityShare.this); + + // Update progess + publishProgress(++mProgressCurrent, lstApp.size() + 1); + setState(appInfo.getUid(), STATE_RUNNING, ActivityShare.this.getString(R.string.msg_loading)); + + // Check if any account allowed + boolean allowedAccounts = false; + AccountManager accountManager = AccountManager.get(ActivityShare.this); + for (Account account : accountManager.getAccounts()) { + String sha1 = Util.sha1(account.name + account.type); + boolean allowed = PrivacyManager.getSettingBool(appInfo.getUid(), Meta.cTypeAccount, sha1, + false); + if (allowed) { + allowedAccounts = true; + break; + } + } + + // Check if any application allowed + boolean allowedApplications = false; + for (ApplicationInfoEx aAppInfo : ApplicationInfoEx.getXApplicationList(ActivityShare.this, + null)) + for (String packageName : aAppInfo.getPackageName()) { + boolean allowed = PrivacyManager.getSettingBool(-appInfo.getUid(), + Meta.cTypeApplication, packageName, false); + if (allowed) { + allowedApplications = true; + break; + } + } + + // Check if any contact allowed + boolean allowedContacts = false; + Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + new String[] { ContactsContract.Contacts._ID }, null, null, null); + if (cursor != null) + try { + while (cursor.moveToNext()) { + long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Contacts._ID)); + boolean allowed = PrivacyManager.getSettingBool(-appInfo.getUid(), + Meta.cTypeContact, Long.toString(id), false); + if (allowed) { + allowedContacts = true; + break; + } + } + } finally { + cursor.close(); + } + + // Get white lists + Map> mapWhitelist = PrivacyManager.listWhitelisted( + appInfo.getUid(), null); + + // Encode restrictions + JSONArray jSettings = new JSONArray(); + for (String restrictionName : PrivacyManager.getRestrictions()) { + boolean restricted = PrivacyManager.getRestrictionEx(appInfo.getUid(), restrictionName, + null).restricted; + // Category + long used = PrivacyManager.getUsage(appInfo.getUid(), restrictionName, null); + JSONObject jRestriction = new JSONObject(); + jRestriction.put("restriction", restrictionName); + jRestriction.put("restricted", restricted); + jRestriction.put("used", used); + if (restrictionName.equals(PrivacyManager.cAccounts)) + jRestriction.put("allowed", allowedAccounts ? 1 : 0); + else if (restrictionName.equals(PrivacyManager.cSystem)) + jRestriction.put("allowed", allowedApplications ? 1 : 0); + else if (restrictionName.equals(PrivacyManager.cContacts)) + jRestriction.put("allowed", allowedContacts ? 1 : 0); + jSettings.put(jRestriction); + + // Methods + for (Hook md : PrivacyManager.getHooks(restrictionName, null)) { + boolean mRestricted = restricted + && PrivacyManager.getRestrictionEx(appInfo.getUid(), restrictionName, + md.getName()).restricted; + long mUsed = PrivacyManager.getUsage(appInfo.getUid(), restrictionName, md.getName()); + + boolean mWhitelisted = false; + if (md.whitelist() != null && mapWhitelist.containsKey(md.whitelist())) + for (Boolean allowed : mapWhitelist.get(md.whitelist()).values()) + if (mRestricted ? allowed : !allowed) { + mWhitelisted = true; + break; + } + + JSONObject jMethod = new JSONObject(); + jMethod.put("restriction", restrictionName); + jMethod.put("method", md.getName()); + jMethod.put("restricted", mRestricted); + jMethod.put("used", mUsed); + jMethod.put("allowed", mWhitelisted ? 1 : 0); + jSettings.put(jMethod); + } + } + + // Get data + String[] license = Util.getProLicenseUnchecked(); + PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + String android_id = Secure + .getString(ActivityShare.this.getContentResolver(), Secure.ANDROID_ID); + + JSONArray appName = new JSONArray(); + for (String name : appInfo.getApplicationName()) + appName.put(name); + + JSONArray pkgName = new JSONArray(); + for (String name : appInfo.getPackageName()) + pkgName.put(name); + + JSONArray pkgVersionName = new JSONArray(); + for (String version : appInfo.getPackageVersionName(ActivityShare.this)) + pkgVersionName.put(version); + + JSONArray pkgVersionCode = new JSONArray(); + for (Integer version : appInfo.getPackageVersionCode(ActivityShare.this)) + pkgVersionCode.put((int) version); + + // Encode package + JSONObject jRoot = new JSONObject(); + jRoot.put("protocol_version", cProtocolVersion); + jRoot.put("android_id", Util.md5(android_id).toLowerCase()); + jRoot.put("android_sdk", Build.VERSION.SDK_INT); + jRoot.put("xprivacy_version", pInfo.versionCode); + jRoot.put("application_name", appName); + jRoot.put("package_name", pkgName); + jRoot.put("package_version_name", pkgVersionName); + jRoot.put("package_version_code", pkgVersionCode); + jRoot.put("settings", jSettings); + if (license != null) { + jRoot.put("email", license[1]); + jRoot.put("signature", license[2]); + } + + if (mAbort) + throw new AbortException(ActivityShare.this); + + setState(appInfo.getUid(), STATE_RUNNING, ActivityShare.this.getString(R.string.menu_submit)); + + // Submit + HttpParams httpParams = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC); + HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC); + HttpClient httpclient = new DefaultHttpClient(httpParams); + + HttpPost httpost = new HttpPost(getBaseURL() + "?format=json&action=submit"); + httpost.setEntity(new ByteArrayEntity(jRoot.toString().getBytes("UTF-8"))); + httpost.setHeader("Accept", "application/json"); + httpost.setHeader("Content-type", "application/json"); + HttpResponse response = httpclient.execute(httpost); + StatusLine statusLine = response.getStatusLine(); + + if (statusLine.getStatusCode() == HttpStatus.SC_OK) { + // Succeeded + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.getEntity().writeTo(out); + out.close(); + JSONObject status = new JSONObject(out.toString("UTF-8")); + if (status.getBoolean("ok")) { + // Mark as shared + PrivacyManager.setSetting(appInfo.getUid(), PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_SHARED)); + setState(appInfo.getUid(), STATE_SUCCESS, null); + } else { + int errno = status.getInt("errno"); + String message = status.getString("error"); + ServerException ex = new ServerException(ActivityShare.this, errno, message); + + // Mark as unregistered + if (errno == ServerException.cErrorNotActivated) { + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingRegistered, + Boolean.toString(false)); + throw ex; + } else + setState(appInfo.getUid(), STATE_FAILURE, ex.getMessage()); + } + } else { + // Failed + response.getEntity().getContent().close(); + throw new IOException(statusLine.getReasonPhrase()); + } + } catch (Throwable ex) { + setState(appInfo.getUid(), STATE_FAILURE, ex.getMessage()); + throw ex; + } + return null; + } catch (ConnectTimeoutException ex) { + return ex; + } catch (HttpHostConnectException ex) { + return ex; + } catch (SocketTimeoutException ex) { + return ex; + } catch (SSLException ex) { + return ex; + } catch (UnknownHostException ex) { + return ex; + } catch (IOException ex) { + return ex; + } catch (Throwable ex) { + Util.bug(null, ex); + return ex; + } finally { + wl.release(); + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + blueStreakOfProgress(values[0], values[1]); + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Throwable result) { + if (!ActivityShare.this.isFinishing()) + done(result); + super.onPostExecute(result); + } + } + + @SuppressLint("InflateParams") + public static boolean registerDevice(final ActivityBase context) { + int userId = Util.getUserId(Process.myUid()); + if (Util.hasProLicense(context) == null + && !PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingRegistered, false)) { + // Get accounts + String email = null; + for (Account account : AccountManager.get(context).getAccounts()) + if ("com.google".equals(account.type)) { + email = account.name; + break; + } + + LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = LayoutInflater.inflate(R.layout.register, null); + final EditText input = (EditText) view.findViewById(R.id.etEmail); + if (email != null) + input.setText(email); + + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setTitle(R.string.msg_register); + alertDialogBuilder.setIcon(context.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setView(view); + alertDialogBuilder.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String email = input.getText().toString(); + if (Patterns.EMAIL_ADDRESS.matcher(email).matches()) + new RegisterTask(context).executeOnExecutor(mExecutor, email); + } + }); + alertDialogBuilder.setNegativeButton(context.getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + + return false; + } + return true; + } + + @SuppressLint("DefaultLocale") + private static class RegisterTask extends AsyncTask { + private ActivityBase mContext; + + public RegisterTask(ActivityBase context) { + mContext = context; + } + + protected Throwable doInBackground(String... params) { + // Get wakelock + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XPrivacy.Register"); + wl.acquire(); + try { + String android_id = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID); + + // Encode message + JSONObject jRoot = new JSONObject(); + jRoot.put("protocol_version", cProtocolVersion); + jRoot.put("email", params[0]); + jRoot.put("android_id", Util.md5(android_id).toLowerCase()); + + // Submit + HttpParams httpParams = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC); + HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC); + HttpClient httpclient = new DefaultHttpClient(httpParams); + + HttpPost httpost = new HttpPost(getBaseURL() + "device?format=json&action=register"); + httpost.setEntity(new ByteArrayEntity(jRoot.toString().getBytes("UTF-8"))); + httpost.setHeader("Accept", "application/json"); + httpost.setHeader("Content-type", "application/json"); + HttpResponse response = httpclient.execute(httpost); + StatusLine statusLine = response.getStatusLine(); + + if (statusLine.getStatusCode() == HttpStatus.SC_OK) { + // Succeeded + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.getEntity().writeTo(out); + out.close(); + JSONObject status = new JSONObject(out.toString("UTF-8")); + if (status.getBoolean("ok")) { + // Mark as registered + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSetting(userId, PrivacyManager.cSettingRegistered, Boolean.toString(true)); + return null; + } else { + int errno = status.getInt("errno"); + String message = status.getString("error"); + throw new ServerException(errno, message); + } + } else { + // Failed + response.getEntity().getContent().close(); + throw new IOException(statusLine.getReasonPhrase()); + } + } catch (ConnectTimeoutException ex) { + return ex; + } catch (HttpHostConnectException ex) { + return ex; + } catch (SocketTimeoutException ex) { + return ex; + } catch (SSLException ex) { + return ex; + } catch (UnknownHostException ex) { + return ex; + } catch (IOException ex) { + return ex; + } catch (Throwable ex) { + Util.bug(null, ex); + return ex; + } finally { + wl.release(); + } + } + + @Override + protected void onPostExecute(Throwable result) { + if (!mContext.isFinishing()) { + String message; + if (result == null) + message = mContext.getString(R.string.msg_registered); + else + message = result.getMessage(); + + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mContext); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(message); + alertDialogBuilder.setIcon(mContext.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(mContext.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + super.onPostExecute(result); + } + } + + public static class UpdateTask extends AsyncTask { + private Context mContext; + private NotificationCompat.Builder builder; + private Notification notification; + private NotificationManager notificationManager; + + public UpdateTask(Context context) { + mContext = context; + builder = new NotificationCompat.Builder(context); + notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + } + + @Override + protected void onPreExecute() { + // Build notification + builder.setSmallIcon(R.drawable.ic_launcher); + builder.setContentTitle(mContext.getString(R.string.app_name)); + builder.setAutoCancel(false); + builder.setOngoing(true); + } + + @Override + @SuppressLint("DefaultLocale") + protected Object doInBackground(Object... args) { + try { + // Notify + builder.setContentText(mContext.getString(R.string.title_update_checking)); + builder.setWhen(System.currentTimeMillis()); + notification = builder.build(); + notificationManager.notify(Util.NOTIFY_UPDATE, notification); + + // Encode package + String[] license = Util.getProLicenseUnchecked(); + int userId = Util.getUserId(Process.myUid()); + boolean test = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingTestVersions, false); + String android_id = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID); + + JSONObject jRoot = new JSONObject(); + jRoot.put("protocol_version", cProtocolVersion); + jRoot.put("android_id", Util.md5(android_id).toLowerCase()); + jRoot.put("android_sdk", Build.VERSION.SDK_INT); + jRoot.put("xprivacy_version", Util.getSelfVersionCode(mContext)); + jRoot.put("xprivacy_version_name", Util.getSelfVersionName(mContext)); + jRoot.put("test_versions", test); + jRoot.put("email", license[1]); + jRoot.put("signature", license[2]); + + // Update + HttpParams httpParams = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC); + HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC); + HttpClient httpclient = new DefaultHttpClient(httpParams); + + HttpPost httpost = new HttpPost(getBaseURL() + "?format=json&action=update"); + httpost.setEntity(new ByteArrayEntity(jRoot.toString().getBytes("UTF-8"))); + httpost.setHeader("Accept", "application/json"); + httpost.setHeader("Content-type", "application/json"); + HttpResponse response = httpclient.execute(httpost); + StatusLine statusLine = response.getStatusLine(); + if (statusLine.getStatusCode() == HttpStatus.SC_OK) { + String contentType = response.getFirstHeader("Content-Type").getValue(); + if ("application/octet-stream".equals(contentType)) { + // Update notification + builder.setContentText(mContext.getString(R.string.title_update_downloading)); + builder.setWhen(System.currentTimeMillis()); + notification = builder.build(); + notificationManager.notify(Util.NOTIFY_UPDATE, notification); + + // Download APK + File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + folder.mkdirs(); + String fileName = response.getFirstHeader("Content-Disposition").getElements()[0] + .getParameterByName("filename").getValue(); + File download = new File(folder, fileName); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(download); + response.getEntity().writeTo(fos); + } finally { + if (fos != null) + fos.close(); + } + + return download; + } else if ("application/json".equals(contentType)) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.getEntity().writeTo(out); + out.close(); + throw new IOException(out.toString("UTF-8")); + } else + throw new IOException(contentType); + } else + return statusLine; + } catch (Throwable ex) { + Util.bug(null, ex); + return ex; + } + } + + @Override + protected void onPostExecute(Object result) { + if (result instanceof StatusLine) { + notificationManager.cancel(Util.NOTIFY_UPDATE); + StatusLine status = (StatusLine) result; + if (status.getStatusCode() == 204) { // No Content + String none = mContext.getString(R.string.title_update_none); + Toast.makeText(mContext, none, Toast.LENGTH_LONG).show(); + } else + Toast.makeText(mContext, status.getStatusCode() + " " + status.getReasonPhrase(), Toast.LENGTH_LONG) + .show(); + + } else if (result instanceof Throwable) { + notificationManager.cancel(Util.NOTIFY_UPDATE); + Throwable ex = (Throwable) result; + Toast.makeText(mContext, ex.toString(), Toast.LENGTH_LONG).show(); + + } else { + File download = (File) result; + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(download), "application/vnd.android.package-archive"); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + // Update notification + builder.setContentText(mContext.getString(R.string.title_update_install)); + builder.setWhen(System.currentTimeMillis()); + builder.setAutoCancel(true); + builder.setOngoing(false); + builder.setContentIntent(pi); + notification = builder.build(); + notificationManager.notify(Util.NOTIFY_UPDATE, notification); + } + + } + } + + // Helper methods + + private void blueStreakOfProgress(Integer current, Integer max) { + // Set up the progress bar + if (mProgressWidth == 0) { + final View vShareProgressEmpty = (View) findViewById(R.id.vShareProgressEmpty); + mProgressWidth = vShareProgressEmpty.getMeasuredWidth(); + } + // Display stuff + if (max == 0) + max = 1; + int width = (int) ((float) mProgressWidth) * current / max; + + View vShareProgressFull = (View) findViewById(R.id.vShareProgressFull); + vShareProgressFull.getLayoutParams().width = width; + vShareProgressFull.invalidate(); + vShareProgressFull.requestLayout(); + } + + private void done(Throwable ex) { + String result = null; + if (ex != null && !(ex instanceof AbortException)) + result = ex.getMessage(); + + // Check result string and display toast with error + if (result != null) + Toast.makeText(this, result, Toast.LENGTH_LONG).show(); + + // Reset progress bar + blueStreakOfProgress(0, 1); + mRunning = false; + + // Update buttons + final Button btnCancel = (Button) findViewById(R.id.btnCancel); + final Button btnOk = (Button) findViewById(R.id.btnOk); + btnCancel.setEnabled(false); + btnOk.setEnabled(true); + + // Handle close + btnOk.setOnClickListener(new Button.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + } + + public void fileChooser() { + Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT); + Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + "/.xprivacy/"); + chooseFile.setDataAndType(uri, "text/xml"); + Intent intent = Intent.createChooser(chooseFile, getString(R.string.app_name)); + startActivityForResult(intent, ACTIVITY_IMPORT_SELECT); + } + + private void showFileName() { + TextView tvDescription = (TextView) findViewById(R.id.tvDescription); + tvDescription.setText(mFileName); + Button btnOk = (Button) findViewById(R.id.btnOk); + btnOk.setEnabled(true); + } + + public static String getBaseURL() { + int userId = Util.getUserId(Process.myUid()); + if (PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingHttps, true)) + return HTTPS_BASE_URL; + else + return HTTP_BASE_URL; + } + + public static String getFileName(Context context, boolean multiple, String packageName) { + File folder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + + ".xprivacy"); + folder.mkdir(); + String fileName; + if (multiple) { + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ROOT); + fileName = String.format("%s_XPrivacy_%s_%s%s.xml", format.format(new Date()), + Util.getSelfVersionName(context), Build.DEVICE, (packageName == null ? "" : "_" + packageName)); + } else + fileName = "XPrivacy.xml"; + return new File(folder + File.separator + fileName).getAbsolutePath(); + } + + // Helper classes + + public static class AbortException extends Exception { + private static final long serialVersionUID = 1L; + + public AbortException(Context context) { + super(context.getString(R.string.msg_aborted)); + } + } + + public static class ServerException extends Exception { + private int mErrorNo; + private Context mContext = null; + private static final long serialVersionUID = 1L; + + public final static int cErrorNotActivated = 206; + public final static int cErrorNoRestrictions = 305; + + public ServerException(int errorno, String message) { + super(message); + mErrorNo = errorno; + } + + public ServerException(Context context, int errorno, String message) { + super(message); + mErrorNo = errorno; + mContext = context; + } + + @Override + @SuppressLint("DefaultLocale") + public String getMessage() { + if (mErrorNo == cErrorNoRestrictions && mContext != null) + return mContext.getString(R.string.msg_no_restrictions); + return String.format("Error %d: %s", mErrorNo, super.getMessage()); + // general: + // 'errno' => 101, 'error' => 'Empty request' + // 'errno' => 102, 'error' => 'Please upgrade to at least ...' + // 'errno' => 103, 'error' => 'Error connecting to database' + // 'errno' => 104, 'error' => 'Unknown action: ...' + + // submit: + // 'errno' => 201, 'error' => 'Not authorized' + // 'errno' => 202, 'error' => 'Android ID missing' + // 'errno' => 203, 'error' => 'Package name missing' + // 'errno' => 204, 'error' => 'Too many packages for application' + // 'errno' => 205, 'error' => 'Error storing restrictions' + // 'errno' => 206, 'error' => 'Device not activated' + // 'errno' => 207, 'error' => 'Unknown category' + + // fetch: + // 'errno' => 301, 'error' => 'Not authorized' + // 'errno' => 303, 'error' => 'Package name missing' + // 'errno' => 304, 'error' => 'Too many packages for application' + // 'errno' => 305, 'error' => 'No restrictions available' + // 'errno' => 306, 'error' => 'Error retrieving restrictions' + // 'errno' => 307, 'error' => 'There is a maximum of ...' + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityUsage.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityUsage.java new file mode 100644 index 0000000..922ae23 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ActivityUsage.java @@ -0,0 +1,459 @@ +package biz.bokhorst.xprivacy; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Process; +import android.support.v4.app.NavUtils; +import android.support.v4.app.TaskStackBuilder; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Filter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +public class ActivityUsage extends ActivityBase { + private boolean mAll = true; + private int mUid; + private String mRestrictionName; + private UsageAdapter mUsageAdapter; + + public static final String cUid = "Uid"; + public static final String cRestriction = "Restriction"; + + private static ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), + new PriorityThreadFactory()); + + private static class PriorityThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.NORM_PRIORITY); + return t; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Check privacy service client + if (!PrivacyService.checkClient()) + return; + + // Set layout + setContentView(R.layout.usagelist); + setSupportActionBar((Toolbar) findViewById(R.id.widgetToolbar)); + + // Get uid + Bundle extras = getIntent().getExtras(); + mUid = (extras == null ? 0 : extras.getInt(cUid, 0)); + mRestrictionName = (extras == null ? null : extras.getString(cRestriction)); + + // Show title + updateTitle(); + + // Start task to get usage data + UsageTask usageTask = new UsageTask(); + usageTask.executeOnExecutor(mExecutor, (Object) null); + + // Up navigation + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + protected void onResume() { + super.onResume(); + if (mUsageAdapter != null) + mUsageAdapter.notifyDataSetChanged(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + if (inflater != null && PrivacyService.checkClient()) { + inflater.inflate(R.menu.usage, menu); + return true; + } else + return false; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.findItem(R.id.menu_whitelists).setVisible(mUid != 0); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + UsageTask usageTask; + switch (item.getItemId()) { + case android.R.id.home: + Intent upIntent = NavUtils.getParentActivityIntent(this); + if (upIntent != null) + if (NavUtils.shouldUpRecreateTask(this, upIntent)) + TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities(); + else + NavUtils.navigateUpTo(this, upIntent); + return true; + + case R.id.menu_usage_all: + mAll = !mAll; + if (mUsageAdapter != null) + mUsageAdapter.getFilter().filter(Boolean.toString(mAll)); + return true; + + case R.id.menu_refresh: + updateTitle(); + usageTask = new UsageTask(); + usageTask.executeOnExecutor(mExecutor, (Object) null); + return true; + + case R.id.menu_clear: + PrivacyManager.deleteUsage(mUid); + usageTask = new UsageTask(); + usageTask.executeOnExecutor(mExecutor, (Object) null); + return true; + + case R.id.menu_whitelists: + if (Util.hasProLicense(this) == null) { + // Redirect to pro page + Util.viewUri(this, ActivityMain.cProUri); + } else { + WhitelistTask whitelistsTask = new WhitelistTask(mUid, null, this); + whitelistsTask.executeOnExecutor(mExecutor, (Object) null); + } + return true; + + case R.id.menu_settings: + Intent intent = new Intent(this, ActivitySettings.class); + startActivity(intent); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + // Tasks + + private class UsageTask extends AsyncTask> { + @Override + protected List doInBackground(Object... arg0) { + List listUsageData = new ArrayList(); + for (PRestriction usageData : PrivacyManager.getUsageList(ActivityUsage.this, mUid, mRestrictionName)) + listUsageData.add(usageData); + return listUsageData; + } + + @Override + protected void onPostExecute(List listUsageData) { + if (!ActivityUsage.this.isFinishing()) { + mUsageAdapter = new UsageAdapter(ActivityUsage.this, R.layout.usageentry, listUsageData); + ListView lvUsage = (ListView) findViewById(R.id.lvUsage); + lvUsage.setAdapter(mUsageAdapter); + mUsageAdapter.getFilter().filter(Boolean.toString(mAll)); + } + + super.onPostExecute(listUsageData); + } + } + + // Adapters + + private class UsageAdapter extends ArrayAdapter { + private boolean mHasProLicense = false; + private List mListUsageData; + private LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + public UsageAdapter(Context context, int textViewResourceId, List objects) { + super(context, textViewResourceId, objects); + mHasProLicense = (Util.hasProLicense(ActivityUsage.this) != null); + mListUsageData = new ArrayList(); + mListUsageData.addAll(objects); + } + + @Override + public Filter getFilter() { + return new UsageFilter(); + } + + private class UsageFilter extends Filter { + public UsageFilter() { + } + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults results = new FilterResults(); + + // Get argument + boolean all = Boolean.parseBoolean((String) constraint); + + // Match applications + List lstResult = new ArrayList(); + for (PRestriction usageData : UsageAdapter.this.mListUsageData) { + if (all ? true : usageData.restricted) + lstResult.add(usageData); + } + + synchronized (this) { + results.values = lstResult; + results.count = lstResult.size(); + } + + return results; + } + + @Override + @SuppressWarnings("unchecked") + protected void publishResults(CharSequence constraint, FilterResults results) { + clear(); + if (results.values == null) + notifyDataSetInvalidated(); + else { + addAll((ArrayList) results.values); + notifyDataSetChanged(); + } + } + } + + private class ViewHolder { + private View row; + private int position; + public LinearLayout llUsage; + public TextView tvTime; + public ImageView imgIcon; + public ImageView imgRestricted; + public TextView tvApp; + public TextView tvRestriction; + public TextView tvParameter; + public TextView tvValue; + + public ViewHolder(View theRow, int thePosition) { + row = theRow; + position = thePosition; + llUsage = (LinearLayout) row.findViewById(R.id.llUsage); + tvTime = (TextView) row.findViewById(R.id.tvTime); + imgIcon = (ImageView) row.findViewById(R.id.imgIcon); + imgRestricted = (ImageView) row.findViewById(R.id.imgRestricted); + tvApp = (TextView) row.findViewById(R.id.tvApp); + tvRestriction = (TextView) row.findViewById(R.id.tvRestriction); + tvParameter = (TextView) row.findViewById(R.id.tvParameter); + tvValue = (TextView) row.findViewById(R.id.tvValue); + } + } + + private class HolderTask extends AsyncTask { + private int position; + private ViewHolder holder; + private PRestriction usageData; + private Drawable icon = null; + private boolean system; + private Hook hook; + + public HolderTask(int thePosition, ViewHolder theHolder, PRestriction theUsageData) { + position = thePosition; + holder = theHolder; + usageData = theUsageData; + } + + @Override + protected Object doInBackground(Object... params) { + if (usageData != null) { + ApplicationInfoEx appInfo = new ApplicationInfoEx(ActivityUsage.this, usageData.uid); + icon = appInfo.getIcon(ActivityUsage.this); + system = appInfo.isSystem(); + hook = PrivacyManager.getHook(usageData.restrictionName, usageData.methodName); + return holder; + } + return null; + } + + @Override + protected void onPostExecute(Object result) { + if (holder.position == position && result != null) { + if (system || (hook != null && hook.isDangerous())) + holder.row.setBackgroundColor(getResources().getColor(getThemed(R.attr.color_dangerous))); + else + holder.row.setBackgroundColor(Color.TRANSPARENT); + holder.imgIcon.setImageDrawable(icon); + holder.imgIcon.setVisibility(View.VISIBLE); + + View.OnClickListener clickListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + PRestriction usageData = mUsageAdapter.getItem(position); + Intent intent = new Intent(ActivityUsage.this, ActivityApp.class); + intent.putExtra(ActivityApp.cUid, usageData.uid); + intent.putExtra(ActivityApp.cRestrictionName, usageData.restrictionName); + intent.putExtra(ActivityApp.cMethodName, usageData.methodName); + startActivity(intent); + } + }; + + View.OnLongClickListener longClickListener = new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + int userId = Util.getUserId(Process.myUid()); + final PRestriction usageData = mUsageAdapter.getItem(position); + final Hook hook = PrivacyManager.getHook(usageData.restrictionName, usageData.methodName); + + boolean isApp = PrivacyManager.isApplication(usageData.uid); + boolean odSystem = PrivacyManager.getSettingBool(userId, + PrivacyManager.cSettingOnDemandSystem, false); + final boolean wnomod = PrivacyManager.getSettingBool(usageData.uid, + PrivacyManager.cSettingWhitelistNoModify, false); + + if ((isApp || odSystem) && hook != null && hook.whitelist() != null + && usageData.extra != null) { + if (Util.hasProLicense(ActivityUsage.this) == null) + Util.viewUri(ActivityUsage.this, ActivityMain.cProUri); + else { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityUsage.this); + alertDialogBuilder.setTitle(R.string.menu_whitelists); + alertDialogBuilder.setMessage(usageData.restrictionName + "/" + + usageData.methodName + "(" + usageData.extra + ")"); + alertDialogBuilder.setIcon(getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(getString(R.string.title_deny), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Deny + PrivacyManager.setSetting(usageData.uid, hook.whitelist(), + usageData.extra, Boolean.toString(false)); + if (!wnomod) + PrivacyManager.updateState(usageData.uid); + } + }); + alertDialogBuilder.setNeutralButton(getString(R.string.title_allow), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Allow + PrivacyManager.setSetting(usageData.uid, hook.whitelist(), + usageData.extra, Boolean.toString(true)); + if (!wnomod) + PrivacyManager.updateState(usageData.uid); + } + }); + alertDialogBuilder.setNegativeButton(getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + return true; + } else + return false; + } + }; + + holder.llUsage.setOnClickListener(clickListener); + holder.tvRestriction.setOnClickListener(clickListener); + holder.llUsage.setOnLongClickListener(longClickListener); + holder.tvRestriction.setOnLongClickListener(longClickListener); + } + } + } + + @Override + @SuppressLint("InflateParams") + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.usageentry, null); + holder = new ViewHolder(convertView, position); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + holder.position = position; + } + + // Get data + PRestriction usageData = getItem(position); + + // Build entry + holder.row.setBackgroundColor(Color.TRANSPARENT); + + Date date = new Date(usageData.time); + SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss", Locale.ROOT); + holder.tvTime.setText(format.format(date)); + + holder.imgIcon.setVisibility(View.INVISIBLE); + holder.imgRestricted.setVisibility(usageData.restricted ? View.VISIBLE : View.INVISIBLE); + holder.tvApp.setText(Integer.toString(usageData.uid)); + holder.tvRestriction.setText(String.format("%s/%s", usageData.restrictionName, usageData.methodName)); + + if (!TextUtils.isEmpty(usageData.extra) && mHasProLicense) { + holder.tvParameter.setText(usageData.extra); + holder.tvParameter.setVisibility(View.VISIBLE); + } else + holder.tvParameter.setVisibility(View.GONE); + + if (usageData.value != null && mHasProLicense) { + holder.tvValue.setText(getString(R.string.title_original) + ": " + usageData.value); + holder.tvValue.setVisibility(View.VISIBLE); + } else + holder.tvValue.setVisibility(View.GONE); + + // Async update + new HolderTask(position, holder, usageData).executeOnExecutor(mExecutor, (Object) null); + + return convertView; + } + } + + // Helpers + + private void updateTitle() { + if (mUid == 0) { + // Get statistics + long count = 0; + long restricted = 0; + double persec = 0; + try { + @SuppressWarnings("rawtypes") + Map statistics = PrivacyService.getClient().getStatistics(); + count = (Long) statistics.get("restriction_count"); + restricted = (Long) statistics.get("restriction_restricted"); + long uptime = (Long) statistics.get("uptime_milliseconds"); + persec = (double) count / (uptime / 1000); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + // Set sub title + getSupportActionBar().setSubtitle(String.format("%d/%d %.2f/s", restricted, count, persec)); + } else + getSupportActionBar().setSubtitle( + TextUtils.join(", ", new ApplicationInfoEx(this, mUid).getApplicationName())); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ApplicationEx.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ApplicationEx.java new file mode 100644 index 0000000..43bf26a --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ApplicationEx.java @@ -0,0 +1,28 @@ +package biz.bokhorst.xprivacy; + +import android.app.Application; +import android.util.Log; + +public class ApplicationEx extends Application { + private Thread.UncaughtExceptionHandler mPrevHandler; + + @Override + public void onCreate() { + super.onCreate(); + + Util.log(null, Log.WARN, "UI started"); + mPrevHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread thread, Throwable ex) { + Util.bug(null, ex); + if (mPrevHandler != null) + mPrevHandler.uncaughtException(thread, ex); + } + }); + } + + public void onDestroy() { + Util.log(null, Log.WARN, "UI stopped"); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ApplicationInfoEx.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ApplicationInfoEx.java new file mode 100644 index 0000000..d054fdf --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/ApplicationInfoEx.java @@ -0,0 +1,303 @@ +package biz.bokhorst.xprivacy; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseArray; + +@SuppressLint("DefaultLocale") +public class ApplicationInfoEx implements Comparable { + private int mUid; + private TreeMap mMapAppInfo = null; + private Map mMapPkgInfo = new HashMap(); + + // Cache + private Boolean mInternet = null; + private Boolean mFrozen = null; + private long mInstallTime = -1; + private long mUpdateTime = -1; + + public static final int STATE_ATTENTION = 0; + public static final int STATE_CHANGED = 1; + public static final int STATE_SHARED = 2; + + public ApplicationInfoEx(Context context, int uid) { + mUid = uid; + mMapAppInfo = new TreeMap(); + PackageManager pm = context.getPackageManager(); + String[] packages = pm.getPackagesForUid(uid); + if (packages != null) + for (String packageName : packages) + try { + ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); + mMapAppInfo.put(pm.getApplicationLabel(appInfo).toString(), appInfo); + } catch (NameNotFoundException ignored) { + } + } + + public static List getXApplicationList(Context context, ProgressDialog dialog) { + // Get references + PackageManager pm = context.getPackageManager(); + + // Get app list + SparseArray mapApp = new SparseArray(); + List listApp = new ArrayList(); + List listAppInfo = pm.getInstalledApplications(PackageManager.GET_META_DATA); + if (dialog != null) + dialog.setMax(listAppInfo.size()); + for (int app = 0; app < listAppInfo.size(); app++) { + if (dialog != null) + dialog.setProgress(app + 1); + + ApplicationInfo appInfo = listAppInfo.get(app); + Util.log(null, Log.INFO, "package=" + appInfo.packageName + " uid=" + appInfo.uid); + + ApplicationInfoEx appInfoEx = new ApplicationInfoEx(context, appInfo.uid); + if (mapApp.get(appInfoEx.getUid()) == null) { + mapApp.put(appInfoEx.getUid(), appInfoEx); + listApp.add(appInfoEx); + } + } + + // Sort result + Collections.sort(listApp); + return listApp; + } + + public ArrayList getApplicationName() { + return new ArrayList(mMapAppInfo.navigableKeySet()); + } + + public String getApplicationName(String packageName) { + for (Entry entry : mMapAppInfo.entrySet()) + if (entry.getValue().packageName.equals(packageName)) + return entry.getKey(); + return ""; + } + + public List getPackageName() { + List listPackageName = new ArrayList(); + for (ApplicationInfo appInfo : mMapAppInfo.values()) + listPackageName.add(appInfo.packageName); + return listPackageName; + } + + private void getPackageInfo(Context context, String packageName) throws NameNotFoundException { + PackageManager pm = context.getPackageManager(); + mMapPkgInfo.put(packageName, pm.getPackageInfo(packageName, 0)); + } + + public List getPackageVersionName(Context context) { + List listVersionName = new ArrayList(); + for (String packageName : this.getPackageName()) + try { + getPackageInfo(context, packageName); + String version = mMapPkgInfo.get(packageName).versionName; + if (version == null) + listVersionName.add("???"); + else + listVersionName.add(version); + } catch (NameNotFoundException ex) { + listVersionName.add(ex.getMessage()); + } + return listVersionName; + } + + public String getPackageVersionName(Context context, String packageName) { + try { + getPackageInfo(context, packageName); + String version = mMapPkgInfo.get(packageName).versionName; + if (version == null) + return "???"; + else + return version; + } catch (NameNotFoundException ex) { + return ex.getMessage(); + } + } + + public List getPackageVersionCode(Context context) { + List listVersionCode = new ArrayList(); + for (String packageName : this.getPackageName()) + try { + getPackageInfo(context, packageName); + listVersionCode.add(mMapPkgInfo.get(packageName).versionCode); + } catch (NameNotFoundException ex) { + listVersionCode.add(0); + } + return listVersionCode; + } + + public Drawable getIcon(Context context) { + // Pick first icon + if (mMapAppInfo.size() > 0) + return mMapAppInfo.firstEntry().getValue().loadIcon(context.getPackageManager()); + else + return new ColorDrawable(Color.TRANSPARENT); + } + + public Bitmap getIconBitmap(Context context) { + if (mMapAppInfo.size() > 0) { + try { + final ApplicationInfo appInfo = mMapAppInfo.firstEntry().getValue(); + if (appInfo.icon == 0) + appInfo.icon = android.R.drawable.sym_def_app_icon; + final Resources resources = context.getPackageManager().getResourcesForApplication(appInfo); + + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(resources, appInfo.icon, options); + + final int pixels = Math.round(Util.dipToPixels(context, 48)); + options.inSampleSize = Util.calculateInSampleSize(options, pixels, pixels); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeResource(resources, appInfo.icon, options); + } catch (NameNotFoundException ex) { + Util.bug(null, ex); + return null; + } + } else + return null; + } + + public boolean hasInternet(Context context) { + if (mInternet == null) { + mInternet = false; + PackageManager pm = context.getPackageManager(); + for (ApplicationInfo appInfo : mMapAppInfo.values()) + if (pm.checkPermission("android.permission.INTERNET", appInfo.packageName) == PackageManager.PERMISSION_GRANTED) { + mInternet = true; + break; + } + } + return mInternet; + } + + public boolean isFrozen(Context context) { + if (mFrozen == null) { + PackageManager pm = context.getPackageManager(); + boolean enabled = false; + for (ApplicationInfo appInfo : mMapAppInfo.values()) + try { + int setting = pm.getApplicationEnabledSetting(appInfo.packageName); + enabled = (enabled || setting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); + enabled = (enabled || setting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + if (enabled) + break; + } catch (IllegalArgumentException ignored) { + } + mFrozen = !enabled; + } + return mFrozen; + } + + public int getUid() { + return mUid; + } + + @SuppressLint("FieldGetter") + public int getState(Context context) { + return Integer.parseInt(PrivacyManager.getSetting(-getUid(), PrivacyManager.cSettingState, + Integer.toString(STATE_CHANGED))); + } + + public long getInstallTime(Context context) { + if (mInstallTime == -1) { + long now = System.currentTimeMillis(); + mInstallTime = now; + for (String packageName : this.getPackageName()) + try { + getPackageInfo(context, packageName); + long time = mMapPkgInfo.get(packageName).firstInstallTime; + if (time < mInstallTime) + mInstallTime = time; + } catch (NameNotFoundException ex) { + } + if (mInstallTime == now) + // no install time, so assume it is old + mInstallTime = 0; + } + return mInstallTime; + } + + public long getUpdateTime(Context context) { + if (mUpdateTime == -1) { + mUpdateTime = 0; + for (String packageName : this.getPackageName()) + try { + getPackageInfo(context, packageName); + long time = mMapPkgInfo.get(packageName).lastUpdateTime; + if (time > mUpdateTime) + mUpdateTime = time; + } catch (NameNotFoundException ex) { + } + } + return mUpdateTime; + } + + @SuppressLint("FieldGetter") + public long getModificationTime(Context context) { + return Long.parseLong(PrivacyManager.getSetting(-getUid(), PrivacyManager.cSettingModifyTime, "0")); + } + + public boolean isSystem() { + boolean mSystem = false; + for (ApplicationInfo appInfo : mMapAppInfo.values()) { + mSystem = ((appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0); + mSystem = mSystem || appInfo.packageName.equals(this.getClass().getPackage().getName()); + mSystem = mSystem || appInfo.packageName.equals(this.getClass().getPackage().getName() + ".pro"); + mSystem = mSystem || appInfo.packageName.equals("de.robv.android.xposed.installer"); + } + return mSystem; + } + + public boolean isShared() { + for (ApplicationInfo appInfo : mMapAppInfo.values()) + if (PrivacyManager.isShared(appInfo.uid)) + return true; + return false; + } + + public boolean isIsolated() { + for (ApplicationInfo appInfo : mMapAppInfo.values()) + if (PrivacyManager.isIsolated(appInfo.uid)) + return true; + return false; + } + + @Override + @SuppressLint("FieldGetter") + public String toString() { + return String.format("%d %s", getUid(), TextUtils.join(", ", getApplicationName())); + } + + @Override + public int compareTo(ApplicationInfoEx other) { + // Locale respecting sorter + Collator collator = Collator.getInstance(Locale.getDefault()); + return collator.compare(TextUtils.join(", ", getApplicationName()), + TextUtils.join(", ", other.getApplicationName())); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/BootReceiver.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/BootReceiver.java new file mode 100644 index 0000000..8dab4d4 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/BootReceiver.java @@ -0,0 +1,72 @@ +package biz.bokhorst.xprivacy; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.support.v4.app.NotificationCompat; + +public class BootReceiver extends BroadcastReceiver { + + @Override + public void onReceive(final Context context, Intent bootIntent) { + // Start boot update + Intent changeIntent = new Intent(); + changeIntent.setClass(context, UpdateService.class); + changeIntent.putExtra(UpdateService.cAction, UpdateService.cActionBoot); + context.startService(changeIntent); + + NotificationManager notificationManager = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + + // Check if Xposed enabled + if (Util.isXposedEnabled() && PrivacyService.checkClient()) + try { + if (PrivacyService.getClient().databaseCorrupt()) { + // Build notification + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context); + notificationBuilder.setSmallIcon(R.drawable.ic_launcher); + notificationBuilder.setContentTitle(context.getString(R.string.app_name)); + notificationBuilder.setContentText(context.getString(R.string.msg_corrupt)); + notificationBuilder.setWhen(System.currentTimeMillis()); + notificationBuilder.setAutoCancel(true); + Notification notification = notificationBuilder.build(); + + // Display notification + notificationManager.notify(Util.NOTIFY_CORRUPT, notification); + } else + context.sendBroadcast(new Intent("biz.bokhorst.xprivacy.action.ACTIVE")); + } catch (Throwable ex) { + Util.bug(null, ex); + } + else { + // Create Xposed installer intent + // @formatter:off + Intent xInstallerIntent = new Intent("de.robv.android.xposed.installer.OPEN_SECTION") + .setPackage("de.robv.android.xposed.installer") + .putExtra("section", "modules") + .putExtra("module", context.getPackageName()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // @formatter:on + + PendingIntent pi = (xInstallerIntent == null ? null : PendingIntent.getActivity(context, 0, + xInstallerIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + + // Build notification + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context); + notificationBuilder.setSmallIcon(R.drawable.ic_launcher); + notificationBuilder.setContentTitle(context.getString(R.string.app_name)); + notificationBuilder.setContentText(context.getString(R.string.app_notenabled)); + notificationBuilder.setWhen(System.currentTimeMillis()); + notificationBuilder.setAutoCancel(true); + if (pi != null) + notificationBuilder.setContentIntent(pi); + Notification notification = notificationBuilder.build(); + + // Display notification + notificationManager.notify(Util.NOTIFY_NOTXPOSED, notification); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/CRestriction.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/CRestriction.java new file mode 100644 index 0000000..ce031ac --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/CRestriction.java @@ -0,0 +1,89 @@ +package biz.bokhorst.xprivacy; + +import java.util.Date; + +public class CRestriction { + private long mExpiry; + private int mUid; + private String mRestrictionName; + private String mMethodName; + private String mExtra; + public boolean restricted; + public boolean asked; + + public CRestriction(int uid, String restrictionName, String methodName, String extra) { + mExpiry = new Date().getTime() + PrivacyManager.cRestrictionCacheTimeoutMs; + mUid = uid; + mRestrictionName = restrictionName; + mMethodName = methodName; + mExtra = extra; + restricted = false; + asked = false; + } + + public CRestriction(PRestriction restriction, String extra) { + mExpiry = new Date().getTime() + PrivacyManager.cRestrictionCacheTimeoutMs; + mUid = restriction.uid; + mRestrictionName = restriction.restrictionName; + mMethodName = restriction.methodName; + mExtra = extra; + restricted = restriction.restricted; + asked = restriction.asked; + } + + public void setExpiry(long time) { + mExpiry = time; + } + + public boolean isExpired() { + return (new Date().getTime() > mExpiry); + } + + public int getUid() { + return mUid; + } + + public void setMethodName(String methodName) { + mMethodName = methodName; + } + + public void setExtra(String extra) { + mExtra = extra; + } + + public boolean isSameMethod(PRestriction restriction) { + // @formatter:off + return (restriction.uid == mUid + && restriction.restrictionName.equals(mRestrictionName) + && (restriction.methodName == null || restriction.methodName.equals(mMethodName))); + // @formatter:on + } + + @Override + public boolean equals(Object obj) { + CRestriction other = (CRestriction) obj; + // @formatter:off + return (mUid == other.mUid + && mRestrictionName.equals(other.mRestrictionName) + && (mMethodName == null ? other.mMethodName == null : mMethodName.equals(other.mMethodName)) + && (mExtra == null ? other.mExtra == null : mExtra.equals(other.mExtra))); + // @formatter:on + } + + @Override + public int hashCode() { + int hash = mUid; + if (mRestrictionName != null) + hash = hash ^ mRestrictionName.hashCode(); + if (mMethodName != null) + hash = hash ^ mMethodName.hashCode(); + if (mExtra != null) + hash = hash ^ mExtra.hashCode(); + return hash; + } + + @Override + public String toString() { + return mUid + ":" + mRestrictionName + "/" + mMethodName + "(" + mExtra + ")" + "=" + restricted + "/" + asked; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/CSetting.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/CSetting.java new file mode 100644 index 0000000..01ebf14 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/CSetting.java @@ -0,0 +1,69 @@ +package biz.bokhorst.xprivacy; + +import java.util.Date; + +import android.util.Log; + +public class CSetting { + private long mTimestamp; + private int mUid; + private String mType; + private String mName; + private String mValue; + + public CSetting(int uid, String type, String name) { + mTimestamp = new Date().getTime(); + mUid = Math.abs(uid); + mType = type; + mName = name; + mValue = null; + if (type == null) { + Util.log(null, Log.WARN, "CSetting null"); + Util.logStack(null, Log.WARN); + } + } + + public void setValue(String value) { + mValue = value; + } + + public boolean willExpire() { + if (mUid == 0) { + if (mName.equals(PrivacyManager.cSettingVersion)) + return false; + if (mName.equals(PrivacyManager.cSettingLog)) + return false; + if (mName.equals(PrivacyManager.cSettingExperimental)) + return false; + } + return true; + } + + public boolean isExpired() { + return (willExpire() ? (mTimestamp + PrivacyManager.cSettingCacheTimeoutMs < new Date().getTime()) : false); + } + + public int getUid() { + return mUid; + } + + public String getValue() { + return mValue; + } + + @Override + public boolean equals(Object obj) { + CSetting other = (CSetting) obj; + return (this.mUid == other.mUid && this.mType.equals(other.mType) && this.mName.equals(other.mName)); + } + + @Override + public int hashCode() { + return mUid ^ mType.hashCode() ^ mName.hashCode(); + } + + @Override + public String toString() { + return mUid + ":" + mType + "/" + mName + "=" + mValue; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/DeviceAdministratorReceiver.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/DeviceAdministratorReceiver.java new file mode 100644 index 0000000..406853f --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/DeviceAdministratorReceiver.java @@ -0,0 +1,20 @@ +package biz.bokhorst.xprivacy; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class DeviceAdministratorReceiver extends DeviceAdminReceiver { + @Override + public void onEnabled(Context context, Intent intent) { + super.onEnabled(context, intent); + Util.log(null, Log.WARN, "Device admin enabled"); + } + + @Override + public void onDisabled(Context context, Intent intent) { + super.onDisabled(context, intent); + Util.log(null, Log.WARN, "Device admin disabled"); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Hook.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Hook.java new file mode 100644 index 0000000..cc3165b --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Hook.java @@ -0,0 +1,319 @@ +package biz.bokhorst.xprivacy; + +import android.annotation.SuppressLint; +import android.os.Build; + +public class Hook implements Comparable { + private String mRestrictionName; + private String mMethodName; + private String[] mPermissions; + private int mSdkFrom; + private int mSdkTo = Integer.MAX_VALUE; + private Version mFrom; + private String mReplacedRestriction; + private String mReplacedMethod; + + private boolean mDangerous = false; + private boolean mRestart = false; + private boolean mNoUsageData = false; + private boolean mNoOnDemand = false; + private String mWhitelist = null; + private boolean mNotify = false; + private int mAOSPSdk = 0; + private int mNotAOSPSdk = 0; + private boolean mUnsafe = false; + private boolean mOptional = false; + private boolean mObsolete = false; + private String mAnnotation = null; + + public Hook(String restrictionName, String methodName) { + mRestrictionName = restrictionName; + mMethodName = methodName; + } + + public Hook(String restrictionName, String methodName, String permissions, int sdk, String from, String replaces) { + mRestrictionName = restrictionName; + mMethodName = methodName; + mPermissions = (permissions == null ? null : permissions.split(",")); + mSdkFrom = sdk; + mFrom = (from == null ? null : new Version(from)); + if (replaces != null) { + int slash = replaces.indexOf('/'); + if (slash > 0) { + mReplacedRestriction = replaces.substring(0, slash); + mReplacedMethod = replaces.substring(slash + 1); + if ("".equals(mReplacedMethod)) + mReplacedMethod = methodName; + } else { + mReplacedRestriction = mRestrictionName; + mReplacedMethod = replaces; + } + } + } + + // Definitions + + public Hook to(int sdk) { + mSdkTo = sdk; + return this; + } + + public Hook dangerous() { + mDangerous = true; + return this; + } + + @SuppressLint("FieldGetter") + public void toggleDangerous() { + String name = String.format("%s.%s.%s", PrivacyManager.cSettingDangerous, this.getRestrictionName(), + this.getName()); + PrivacyManager.setSetting(0, name, Boolean.toString(!isDangerous())); + } + + public Hook restart() { + mRestart = true; + return this; + } + + public Hook noUsageData() { + mNoUsageData = true; + return this; + } + + public Hook noOnDemand() { + mNoOnDemand = true; + return this; + } + + public Hook whitelist(String whitelist) { + mWhitelist = whitelist; + return this; + } + + public Hook doNotify() { + mNotify = true; + return this; + } + + public Hook AOSP(int sdk) { + mAOSPSdk = sdk; + return this; + } + + public Hook notAOSP(int sdk) { + mNotAOSPSdk = sdk; + if (!PrivacyManager.cIPC.equals(mRestrictionName)) + mUnsafe = true; + return this; + } + + public Hook unsafe() { + mUnsafe = true; + return this; + } + + protected Hook optional() { + mOptional = true; + return this; + } + + protected Hook obsolete() { + mObsolete = true; + return this; + } + + public void annotate(String text) { + mAnnotation = text; + } + + // Getters + + public String getRestrictionName() { + return mRestrictionName; + } + + public String getName() { + return mMethodName; + } + + public String[] getPermissions() { + return mPermissions; + } + + public boolean isAvailable() { + if (mObsolete) + return false; + if (Build.VERSION.SDK_INT < mSdkFrom) + return false; + if (Build.VERSION.SDK_INT > mSdkTo) + return false; + if (mAOSPSdk > 0 && !isAOSP(mAOSPSdk)) + return false; + if (mNotAOSPSdk > 0 && isAOSP(mNotAOSPSdk)) + return false; + return true; + } + + public static boolean isAOSP(int sdk) { + if (!PrivacyManager.cVersion3) + return false; + if (Build.VERSION.SDK_INT >= sdk) { + if ("true".equals(System.getenv("XPrivacy.AOSP"))) + return true; + if (Build.DISPLAY == null || Build.HOST == null) + return false; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + // @formatter:off + return ( + isAOSP() || + isCyanogenMod() || + isOmni() || + isMIUI() || + isSlim() || + isParanoidAndroid() || + isCarbon() || + isDirtyUnicorns() || + isLiquidSmooth() || + isAndroidRevolutionHD() || + isMahdi() || + isOmega() + ); + // @formatter:on + else + return isAOSP(); + } else + return false; + } + + public static boolean isAOSP() { + return Build.HOST.endsWith(".google.com"); + } + + public static boolean isCyanogenMod() { + return Build.HOST.equals("cyanogenmod") || Build.DISPLAY.startsWith("cm_"); + } + + public static boolean isOmni() { + return Build.DISPLAY.startsWith("slim"); + } + + public static boolean isMIUI() { + return Build.HOST.equals("xiaomi"); + } + + public static boolean isSlim() { + return Build.DISPLAY.startsWith("omni"); + } + + public static boolean isParanoidAndroid() { + return Build.HOST.startsWith("paranoid") || Build.DISPLAY.startsWith("pa_"); + } + + public static boolean isCarbon() { + return Build.DISPLAY.startsWith("carbon"); + } + + public static boolean isDirtyUnicorns() { + return Build.DISPLAY.startsWith("du_"); + } + + public static boolean isLiquidSmooth() { + return Build.DISPLAY.startsWith("liquid_"); + } + + public static boolean isAndroidRevolutionHD() { + return Build.DISPLAY.startsWith("Android Revolution HD"); + } + + public static boolean isMahdi() { + return Build.HOST.startsWith("mahdi"); + } + + public static boolean isOmega() { + return Build.DISPLAY.startsWith("Omega"); + } + + public Version getFrom() { + return mFrom; + } + + public String getReplacedRestriction() { + return mReplacedRestriction; + } + + public String getReplacedMethod() { + return mReplacedMethod; + } + + @SuppressLint("FieldGetter") + public boolean isDangerous() { + String name = String.format("%s.%s.%s", PrivacyManager.cSettingDangerous, this.getRestrictionName(), + this.getName()); + return PrivacyManager.getSettingBool(0, name, mDangerous); + } + + public boolean isDangerousDefined() { + return mDangerous; + } + + public boolean isRestartRequired() { + return mRestart; + } + + public boolean hasUsageData() { + return !mNoUsageData; + } + + public boolean canOnDemand() { + return !mNoOnDemand; + } + + public String whitelist() { + return mWhitelist; + } + + public boolean shouldNotify() { + return mNotify; + } + + public boolean isUnsafe() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + return false; + if (PrivacyManager.cVersion3) + return mUnsafe; + else + return false; + } + + public boolean isOptional() { + return mOptional; + } + + public String getAnnotation() { + return mAnnotation; + } + + // Comparison + + @Override + public int hashCode() { + return (mRestrictionName.hashCode() ^ mMethodName.hashCode()); + } + + @Override + public boolean equals(Object obj) { + Hook other = (Hook) obj; + return (mRestrictionName.equals(other.mRestrictionName) && mMethodName.equals(other.mMethodName)); + } + + @Override + public int compareTo(Hook another) { + int x = mRestrictionName.compareTo(another.mRestrictionName); + return (x == 0 ? mMethodName.compareTo(another.mMethodName) : x); + } + + @Override + public String toString() { + return String.format("%s/%s", mRestrictionName, mMethodName); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/IPrivacyService.aidl b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/IPrivacyService.aidl new file mode 100644 index 0000000..cb8d0a2 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/IPrivacyService.aidl @@ -0,0 +1,33 @@ +package biz.bokhorst.xprivacy; + +import biz.bokhorst.xprivacy.PRestriction; +import biz.bokhorst.xprivacy.PSetting; + +interface IPrivacyService { + int getVersion(); + List /* String */ check(); + boolean databaseCorrupt(); + void reportError(String message); + Map getStatistics(); + + void setRestriction(in PRestriction restriction); + void setRestrictionList(in List listRestriction); + PRestriction getRestriction(in PRestriction restriction, boolean usage, String secret); + List getRestrictionList(in PRestriction selector); + boolean isRestrictionSet(in PRestriction restriction); + void deleteRestrictions(int uid, String restrictionName); + + long getUsage(in List restriction); + List getUsageList(int uid, String restrictionName); + void deleteUsage(int uid); + + void setSetting(in PSetting setting); + void setSettingList(in List listSetting); + PSetting getSetting(in PSetting setting); + List getSettingList(in PSetting selector); + void deleteSettings(int uid); + + void clear(); + void flush(); + void dump(int uid); +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/IniFile.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/IniFile.java new file mode 100644 index 0000000..3a13902 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/IniFile.java @@ -0,0 +1,37 @@ +package biz.bokhorst.xprivacy; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class IniFile { + + private Map mIni = new HashMap(); + + public IniFile(File file) throws IOException { + String line; + Pattern pattern = Pattern.compile("\\s*([^=]*)=(.*)"); + FileReader fr = new FileReader(file); + BufferedReader br = new BufferedReader(fr); + while ((line = br.readLine()) != null) + if (!line.startsWith("#")) { + Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + String key = matcher.group(1).trim(); + String value = matcher.group(2).trim(); + mIni.put(key, value); + } + } + br.close(); + fr.close(); + } + + public String get(String key, String defaultvalue) { + return mIni.get(key); + } +} \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Meta.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Meta.java new file mode 100644 index 0000000..4f369be --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Meta.java @@ -0,0 +1,684 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.content.res.Resources; +import android.nfc.NfcAdapter; +import android.os.Build; +import android.provider.MediaStore; +import android.provider.Telephony; +import android.telephony.TelephonyManager; +import android.util.Log; + +@SuppressLint("InlinedApi") +public class Meta { + private static boolean mAnnotated = false; + private static List mListHook = new ArrayList(); + + public final static String cTypeAccount = "Account"; + public final static String cTypeAccountHash = "AccountHash"; + public final static String cTypeApplication = "Application"; + public final static String cTypeContact = "Contact"; + public final static String cTypeTemplate = "Template"; + public final static String cTypeTemplateName = "TemplateName"; + + public final static String cTypeAddress = "Address"; + public final static String cTypeAction = "Action"; + public final static String cTypeCommand = "Command"; + public final static String cTypeFilename = "Filename"; + public final static String cTypeIPAddress = "IPAddress"; + public final static String cTypeLibrary = "Library"; + public final static String cTypeMethod = "Method"; + public final static String cTypePermission = "Permission"; + public final static String cTypeProc = "Proc"; + public final static String cTypeTransaction = "Transaction"; + public final static String cTypeUrl = "Url"; + + public static boolean isWhitelist(String type) { + return (cTypeAddress.equals(type) || cTypeAction.equals(type) || cTypeCommand.equals(type) + || cTypeFilename.equals(type) || cTypeIPAddress.equals(type) || cTypeLibrary.equals(type) + || cTypeMethod.equals(type) || cTypePermission.equals(type) || cTypeProc.equals(type) + || cTypeTransaction.equals(type) || cTypeUrl.equals(type)); + } + + public static List get() { + // http://developer.android.com/reference/android/Manifest.permission.html + if (mListHook.size() > 0) + return mListHook; + + // @formatter:off + mListHook.add(new Hook("accounts", "addOnAccountsUpdatedListener", "GET_ACCOUNTS", 5, null, null).notAOSP(19)); + mListHook.add(new Hook("accounts", "blockingGetAuthToken", "USE_CREDENTIALS", 5, null, null).dangerous().unsafe()); + mListHook.add(new Hook("accounts", "getAccounts", "GET_ACCOUNTS", 5, null, null).notAOSP(19)); + mListHook.add(new Hook("accounts", "getAccountsByType", "GET_ACCOUNTS", 5, null, null).notAOSP(19)); + mListHook.add(new Hook("accounts", "getAccountsByTypeAndFeatures", "GET_ACCOUNTS", 5, null, null).notAOSP(19)); + mListHook.add(new Hook("accounts", "getAuthToken", "USE_CREDENTIALS", 5, null, null).unsafe().dangerous()); + mListHook.add(new Hook("accounts", "getAuthTokenByFeatures", "MANAGE_ACCOUNTS", 5, null, null).unsafe().dangerous()); + mListHook.add(new Hook("accounts", "hasFeatures", "GET_ACCOUNTS", 8, null, null).unsafe().dangerous()); + mListHook.add(new Hook("accounts", "getAccountsByTypeForPackage", "GET_ACCOUNTS", 18, null, null).notAOSP(19)); + + mListHook.add(new Hook("accounts", "getTokenGoogle", "GET_ACCOUNTS", 1, null, null).unsafe().dangerous().optional()); + mListHook.add(new Hook("accounts", "getTokenWithNotificationGoogle", "GET_ACCOUNTS", 1, null, null).unsafe().dangerous().optional()); + + mListHook.add(new Hook("accounts", "getAuthenticatorTypes", "GET_ACCOUNTS", 5, "1.99.24", null).unsafe().dangerous()); + mListHook.add(new Hook("accounts", "getCurrentSync", "READ_SYNC_SETTINGS", 8, "1.99.24", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("accounts", "getCurrentSyncs", "READ_SYNC_SETTINGS", 11, "1.99.24", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("accounts", "getSyncAdapterTypes", "", 5, "1.99.24", null).unsafe().dangerous()); + + mListHook.add(new Hook("accounts", "Srv_getAccounts", "GET_ACCOUNTS", 19, "2.99", "getAccounts").AOSP(19)); + mListHook.add(new Hook("accounts", "Srv_getAccountsAsUser", "GET_ACCOUNTS", 19, "2.99", null).AOSP(19)); + mListHook.add(new Hook("accounts", "Srv_getAccountsByFeatures", "GET_ACCOUNTS", 19, "2.99", "getAccountsByTypeAndFeatures").AOSP(19)); + mListHook.add(new Hook("accounts", "Srv_getAccountsForPackage", "GET_ACCOUNTS", 19, "3.5.6", null).AOSP(19)); + mListHook.add(new Hook("accounts", "Srv_getSharedAccountsAsUser", "GET_ACCOUNTS", 19, "2.99", null).AOSP(19)); + mListHook.add(new Hook("accounts", "Srv_getCurrentSyncs", "READ_SYNC_SETTINGS", 19, "2.99", "getCurrentSyncs").AOSP(19)); + mListHook.add(new Hook("accounts", "Srv_getCurrentSyncsAsUser", "READ_SYNC_SETTINGS", 21, "3.5.6", null).AOSP(21)); + + mListHook.add(new Hook("browser", "BrowserProvider2", "com.android.browser.permission.READ_HISTORY_BOOKMARKS,GLOBAL_SEARCH", 1, null, null)); + mListHook.add(new Hook("browser", "Downloads", "ACCESS_DOWNLOAD_MANAGER,ACCESS_DOWNLOAD_MANAGER_ADVANCED,ACCESS_ALL_DOWNLOADS", 1, "1.99.43", null).dangerous()); + + mListHook.add(new Hook("calendar", "CalendarProvider2", "READ_CALENDAR,WRITE_CALENDAR", 1, null, null)); + + mListHook.add(new Hook("calling", "sendDataMessage", "SEND_SMS", 4, null, null).notAOSP(19).whitelist(cTypeAddress).doNotify()); + mListHook.add(new Hook("calling", "sendMultimediaMessage", "SEND_SMS", 21, "3.5.6", null).doNotify()); + mListHook.add(new Hook("calling", "sendMultipartTextMessage", "SEND_SMS", 4, null, null).notAOSP(19).whitelist(cTypeAddress).doNotify()); + mListHook.add(new Hook("calling", "sendTextMessage", "SEND_SMS", 4, null, null).notAOSP(19).whitelist(cTypeAddress).doNotify()); + + mListHook.add(new Hook("calling", "Srv_sendData", "SEND_SMS", 19, "2.99", "sendDataMessage").AOSP(19).whitelist(cTypeAddress).doNotify()); + mListHook.add(new Hook("calling", "Srv_sendMultipartText", "SEND_SMS", 19, "2.99", "sendMultipartTextMessage").AOSP(19).whitelist(cTypeAddress).doNotify()); + mListHook.add(new Hook("calling", "Srv_sendText", "SEND_SMS", 19, "2.99", "sendTextMessage").AOSP(19).whitelist(cTypeAddress).doNotify()); + + mListHook.add(new Hook("calling", TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, "SEND_RESPOND_VIA_MESSAGE", 18, null, null).doNotify()); + mListHook.add(new Hook("calling", Intent.ACTION_CALL, "CALL_PHONE", 10, null, null).doNotify()); + mListHook.add(new Hook("calling", Intent.ACTION_DIAL, "", 10, "2.2.2", null).doNotify()); + mListHook.add(new Hook("calling", Intent.ACTION_NEW_OUTGOING_CALL, "PROCESS_OUTGOING_CALLS", 10, "2.1.23", "phone/android.intent.action.NEW_OUTGOING_CALL").doNotify()); + mListHook.add(new Hook("calling", "CallLogProvider", "READ_CALL_LOG,WRITE_CALL_LOG", 1, "2.1.23", "phone/CallLogProvider")); + + mListHook.add(new Hook("calling", "SIP.isApiSupported", "USE_SIP", 9, null, null).unsafe().doNotify()); + mListHook.add(new Hook("calling", "SIP.isSipWifiOnly", "USE_SIP", 9, null, null).unsafe().doNotify()); + mListHook.add(new Hook("calling", "SIP.isVoipSupported", "USE_SIP", 9, null, null).unsafe().doNotify()); + mListHook.add(new Hook("calling", "SIP.newInstance", "USE_SIP", 9, null, null).unsafe().doNotify()); + + mListHook.add(new Hook("clipboard", "addPrimaryClipChangedListener", "", 11, null, null).notAOSP(19)); + mListHook.add(new Hook("clipboard", "getPrimaryClip", "", 11, null, null).notAOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "getPrimaryClipDescription", "", 11, null, null).notAOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "getText", "", 10, null, null).notAOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "hasPrimaryClip", "", 11, null, null).notAOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "hasText", "", 10, null, null).notAOSP(19).doNotify()); + + mListHook.add(new Hook("clipboard", "Srv_addPrimaryClipChangedListener", "", 19, "2.99", "addPrimaryClipChangedListener").AOSP(19)); + mListHook.add(new Hook("clipboard", "Srv_getPrimaryClip", "", 19, "2.99", "getPrimaryClip").AOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "Srv_getPrimaryClipDescription", "", 19, "2.99", "getPrimaryClipDescription").AOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "Srv_hasClipboardText", "", 19, "2.99", "hasText").AOSP(19).doNotify()); + mListHook.add(new Hook("clipboard", "Srv_hasPrimaryClip", "", 19, "2.99", "hasPrimaryClip").AOSP(19).doNotify()); + + mListHook.add(new Hook("contacts", "contacts/contacts", "READ_CONTACTS,WRITE_CONTACTS", 1, null, null)); + mListHook.add(new Hook("contacts", "contacts/data", "READ_CONTACTS,WRITE_CONTACTS", 1, null, null)); + mListHook.add(new Hook("contacts", "contacts/people", "READ_CONTACTS,WRITE_CONTACTS", 1, "1.99.46", null)); + mListHook.add(new Hook("contacts", "contacts/phone_lookup", "READ_CONTACTS,WRITE_CONTACTS", 1, null, null)); + mListHook.add(new Hook("contacts", "contacts/profile", "READ_PROFILE,WRITE_PROFILE", 1, "1.99.38", null).dangerous()); + mListHook.add(new Hook("contacts", "contacts/raw_contacts", "READ_CONTACTS,WRITE_CONTACTS", 1, null, null)); + mListHook.add(new Hook("contacts", "ContactsProvider2", "READ_CONTACTS,WRITE_CONTACTS,READ_PROFILE,WRITE_PROFILE", 1, "1.99.38", null).dangerous()); + mListHook.add(new Hook("contacts", "IccProvider", "READ_CONTACTS,WRITE_CONTACTS", 1, "1.99.38", null)); + + mListHook.add(new Hook("dictionary", "UserDictionary", "READ_USER_DICTIONARY", 1, null, null)); + + mListHook.add(new Hook("email", "EMailProvider", "com.android.email.permission.ACCESS_PROVIDER", 1, null, null)); + mListHook.add(new Hook("email", "GMailProvider", "com.google.android.gm.permission.READ_CONTENT_PROVIDER", 8, "1.99.20", null)); + + mListHook.add(new Hook("identification", "%hostname", "", 1, null, null).unsafe()); + mListHook.add(new Hook("identification", "%imei", "", 1, null, null).unsafe()); + mListHook.add(new Hook("identification", "%macaddr", "", 1, null, null).unsafe()); + mListHook.add(new Hook("identification", "%serialno", "", 1, null, null).unsafe()); + mListHook.add(new Hook("identification", "%cid", "", 1, null, null).unsafe()); + mListHook.add(new Hook("identification", "/proc", "", 1, "1.7", null).unsafe().dangerous().whitelist(cTypeProc)); + mListHook.add(new Hook("identification", "/system/build.prop", "", 1, "1.9.9", null).unsafe().dangerous()); + mListHook.add(new Hook("identification", "/sys/block/.../cid", "", 1, null, null).unsafe().dangerous()); + mListHook.add(new Hook("identification", "/sys/class/.../cid", "", 1, null, null).unsafe().dangerous()); + mListHook.add(new Hook("identification", "AdvertisingId", "", 1, null, null).unsafe().optional()); + + mListHook.add(new Hook("identification", "getString", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook("identification", "InputDevice.getDescriptor", "", 16, "2.2.2", "getDescriptor").unsafe()); + mListHook.add(new Hook("identification", "InputDevice.getName", "", 9, null, null).unsafe()); + mListHook.add(new Hook("identification", "GservicesProvider", "com.google.android.providers.gsf.permission.READ_GSERVICES,com.google.android.providers.gsf.permission.WRITE_GSERVICES", 1, null, null).dangerous()); + mListHook.add(new Hook("identification", "SERIAL", "", 1, null, null).restart().noUsageData()); + + mListHook.add(new Hook("identification", "USB.getDeviceId", "", 12, "2.1.7", null).unsafe()); + mListHook.add(new Hook("identification", "USB.getDeviceName", "", 12, "2.1.7", null).unsafe()); + mListHook.add(new Hook("identification", "USB.getSerialNumber", "", 20, "2.1.17", null).unsafe()); + + mListHook.add(new Hook("identification", "Srv_Android_ID", "", 19, "2.99", "getString").AOSP(19)); + + mListHook.add(new Hook("identification", "Cast.getDeviceId", "", 1, "3.5.11", null).unsafe()); + mListHook.add(new Hook("identification", "Cast.getIpAddress", "", 1, "3.5.11", null).unsafe()); + + // java.net.NetworkInterface + mListHook.add(new Hook("internet", "NetworkInterface.getByIndex", "INTERNET", 19, "2.2.2", null).unsafe()); + mListHook.add(new Hook("internet", "NetworkInterface.getByInetAddress", "INTERNET", 1, "2.2.2", "getByInetAddress").unsafe()); + mListHook.add(new Hook("internet", "NetworkInterface.getByName", "INTERNET", 1, "2.2.2", "getByName").unsafe().dangerous().whitelist(cTypeIPAddress)); + mListHook.add(new Hook("internet", "NetworkInterface.getNetworkInterfaces", "INTERNET", 1, "2.2.2", "getNetworkInterfaces").unsafe()); + + mListHook.add(new Hook("internet", "inet", "INTERNET", 1, null, null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("internet", "inet_admin", "NET_ADMIN", 1, "2.1.1", null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("internet", "inet_bw", "READ_NETWORK_USAGE_HISTORY,MODIFY_NETWORK_ACCOUNTING", 1, "2.1.1", null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("internet", "inet_vpn", "NET_TUNNELING", 1, "2.1.1", null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("internet", "inet_mesh", "LOOP_RADIO", 1, "2.1.1", null).dangerous().restart().noUsageData()); + + // android.net.ConnectivityManager + mListHook.add(new Hook("internet", "Connectivity.getActiveNetworkInfo", null, 1, "2.2.2", "getActiveNetworkInfo").unsafe().dangerous()); + mListHook.add(new Hook("internet", "Connectivity.getAllNetworkInfo", null, 1, "2.2.2", "getAllNetworkInfo").unsafe()); + mListHook.add(new Hook("internet", "Connectivity.getNetworkInfo", null, 1, "2.2.2", "getNetworkInfo").unsafe().dangerous()); + + // android.net.NetworkInfo + mListHook.add(new Hook("internet", "NetworkInfo.getDetailedState", null, 1, "2.2.2", "getDetailedState").unsafe()); + mListHook.add(new Hook("internet", "NetworkInfo.getState", null, 1, "2.2.2", "getState").unsafe()); + mListHook.add(new Hook("internet", "NetworkInfo.isConnected", null, 1, "2.2.2", "isConnected").unsafe()); + mListHook.add(new Hook("internet", "NetworkInfo.isConnectedOrConnecting", null, 1, "2.2.2", "isConnectedOrConnecting").unsafe()); + + // android.net.wifi.WifiManager + mListHook.add(new Hook("internet", "WiFi.getConnectionInfo", null, 10, "2.2.2", "getConnectionInfo").notAOSP(19)); + mListHook.add(new Hook("internet", "WiFi.Srv_getConnectionInfo", null, 10, "2.99", "WiFi.getConnectionInfo").AOSP(19)); + + // java.net.InetAddress + mListHook.add(new Hook("internet", "InetAddress.getAllByName", "INTERNET", 1, null, null).unsafe().dangerous().whitelist(cTypeIPAddress)); + mListHook.add(new Hook("internet", "InetAddress.getAllByNameOnNet", "INTERNET", 21, "3.5.6", null).unsafe().dangerous().whitelist(cTypeIPAddress)); + mListHook.add(new Hook("internet", "InetAddress.getByAddress", "INTERNET", 1, null, null).unsafe().dangerous().whitelist(cTypeIPAddress)); + mListHook.add(new Hook("internet", "InetAddress.getByName", "INTERNET", 1, null, null).unsafe().dangerous().whitelist(cTypeIPAddress)); + mListHook.add(new Hook("internet", "InetAddress.getByNameOnNet", "INTERNET", 21, "3.5.6", null).unsafe().dangerous().whitelist(cTypeIPAddress)); + + // android.net.IpPrefix + mListHook.add(new Hook("internet", "IpPrefix.getAddress", null, 21, "3.5.6", null).dangerous().unsafe()); + mListHook.add(new Hook("internet", "IpPrefix.getRawAddress", null, 21, "3.5.6", null).dangerous().unsafe()); + + // android.net.LinkProperties + mListHook.add(new Hook("internet", "LinkProperties.getAddresses", null, 19, "3.5.6", null).dangerous().unsafe()); + mListHook.add(new Hook("internet", "LinkProperties.getAllAddresses", null, 19, "3.5.6", null).dangerous().unsafe()); + mListHook.add(new Hook("internet", "LinkProperties.getAllLinkAddresses", null, 19, "3.5.6", null).dangerous().unsafe()); + mListHook.add(new Hook("internet", "LinkProperties.getLinkAddresses", null, 19, "3.5.6", null).dangerous().unsafe()); + mListHook.add(new Hook("internet", "LinkProperties.getStackedLinks", null, 19, "3.5.6", null).dangerous().unsafe()); + + mListHook.add(new Hook("internet", "connect", null, 1, "1.99.45", null).unsafe().dangerous().whitelist(cTypeIPAddress)); + + mListHook.add(new Hook("ipc", "Binder", "", 1, "2.1.21", null).notAOSP(19).dangerous().whitelist(cTypeTransaction)); + + mListHook.add(new Hook("location", "addGeofence", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 17, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "addGpsStatusListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 3, "2.1.17", null).notAOSP(19)); + mListHook.add(new Hook("location", "addNmeaListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 5, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "addProximityAlert", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "getAllProviders", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.20", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("location", "getBestProvider", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.20", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("location", "getGpsStatus", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 3, "1.99.29", null).notAOSP(19)); + mListHook.add(new Hook("location", "getLastKnownLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "getProviders", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "1.99.1", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("location", "isProviderEnabled", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "1.99.1", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("location", "requestLocationUpdates", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).restart().notAOSP(19)); + mListHook.add(new Hook("location", "requestSingleUpdate", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 9, null, null).restart().notAOSP(19)); + mListHook.add(new Hook("location", "sendExtraCommand", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 3, null, null).notAOSP(19)); + + mListHook.add(new Hook("location", "Srv_requestGeofence", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 17, "2.99", "addGeofence").AOSP(19)); + mListHook.add(new Hook("location", "Srv_addGpsStatusListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 3, "2.99", "addGpsStatusListener").AOSP(19)); + mListHook.add(new Hook("location", "Srv_getAllProviders", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "getAllProviders").AOSP(19).dangerous()); + mListHook.add(new Hook("location", "Srv_getBestProvider", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "getBestProvider").AOSP(19).dangerous()); + mListHook.add(new Hook("location", "Srv_getProviders", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "getProviders").AOSP(19).dangerous()); + mListHook.add(new Hook("location", "Srv_isProviderEnabled", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "isProviderEnabled").AOSP(19).dangerous()); + mListHook.add(new Hook("location", "Srv_getLastLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "getLastKnownLocation").AOSP(19)); + mListHook.add(new Hook("location", "Srv_requestLocationUpdates", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "requestLocationUpdates").restart().AOSP(19)); + mListHook.add(new Hook("location", "Srv_sendExtraCommand", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 3, "2.99", "sendExtraCommand").AOSP(19)); + mListHook.add(new Hook("location", "Srv_addGpsMeasurementsListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 21, "3.5.6", null).AOSP(21)); + mListHook.add(new Hook("location", "Srv_addGpsNavigationMessageListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 21, "3.5.6", null).AOSP(21)); + + mListHook.add(new Hook("location", "enableLocationUpdates", "CONTROL_LOCATION_UPDATES", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "getAllCellInfo", "ACCESS_COARSE_UPDATES", 17, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "getCellLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "getNeighboringCellInfo", "ACCESS_COARSE_UPDATES", 3, null, null).notAOSP(19)); + + mListHook.add(new Hook("location", "Srv_enableLocationUpdates", "CONTROL_LOCATION_UPDATES", 10, "2.99", "enableLocationUpdates").AOSP(19)); + mListHook.add(new Hook("location", "Srv_enableLocationUpdatesForSubscriber", "CONTROL_LOCATION_UPDATES", 21, "3.5.6", null).AOSP(21)); + mListHook.add(new Hook("location", "Srv_getAllCellInfo", "ACCESS_COARSE_UPDATES", 17, "2.99", "getAllCellInfo").AOSP(19)); + mListHook.add(new Hook("location", "Srv_getCellLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99", "getCellLocation").AOSP(19)); + mListHook.add(new Hook("location", "Srv_getNeighboringCellInfo", "ACCESS_COARSE_UPDATES", 3, "2.99", "getNeighboringCellInfo").AOSP(19)); + + mListHook.add(new Hook("location", "WiFi.getScanResults", "ACCESS_WIFI_STATE", 1, "2.2.2", "getScanResults").notAOSP(19).dangerous()); + mListHook.add(new Hook("location", "WiFi.Srv_getScanResults", "ACCESS_WIFI_STATE", 1, "2.99", "WiFi.getScanResults").AOSP(19).dangerous()); + + mListHook.add(new Hook("location", "listen", "ACCESS_COARSE_LOCATION", 1, null, null).notAOSP(19)); + mListHook.add(new Hook("location", "Srv_listen", "ACCESS_COARSE_LOCATION", 1, null, null).AOSP(19)); + + mListHook.add(new Hook("location", "GMS.addGeofences", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).unsafe().optional()); + mListHook.add(new Hook("location", "GMS.getLastLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).unsafe().optional()); + mListHook.add(new Hook("location", "GMS.requestLocationUpdates", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, null, null).unsafe().optional()); + mListHook.add(new Hook("location", "GMS.requestActivityUpdates", "com.google.android.gms.permission.ACTIVITY_RECOGNITION", 1, null, null).unsafe()); + + mListHook.add(new Hook("location", "GMS5.getLastLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99.26", null).unsafe().optional()); + mListHook.add(new Hook("location", "GMS5.requestLocationUpdates", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.99.26", null).unsafe().optional()); + mListHook.add(new Hook("location", "GMS5.requestActivityUpdates", "com.google.android.gms.permission.ACTIVITY_RECOGNITION", 1, "2.99.26", null).unsafe()); + + mListHook.add(new Hook("location", "GMS5.getCurrentPlace", "ACCESS_FINE_LOCATION", 1, "3.6.9", null).unsafe()); + + mListHook.add(new Hook("location", "MapV1.enableMyLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + + mListHook.add(new Hook("location", "MapV2.getMyLocation", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + mListHook.add(new Hook("location", "MapV2.getPosition", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + mListHook.add(new Hook("location", "MapV2.setLocationSource", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + mListHook.add(new Hook("location", "MapV2.setOnMapClickListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + mListHook.add(new Hook("location", "MapV2.setOnMapLongClickListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + mListHook.add(new Hook("location", "MapV2.setOnMyLocationChangeListener", "ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION", 1, "2.1.25", null).unsafe().optional()); + + mListHook.add(new Hook("media", "Audio.startRecording", "RECORD_AUDIO", 3, "2.2.3", "startRecording").unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.setPreviewCallback", "CAMERA", 1, "2.99.21", "setPreviewCallback").unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.setPreviewCallbackWithBuffer", "CAMERA", 8, "2.99.21", null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.setPreviewDisplay", "CAMERA", 1, "2.99.21", null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.setPreviewTexture", "CAMERA", 11, "2.99.21", null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.setOneShotPreviewCallback", "CAMERA", 11, "2.99.21", null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.startPreview", "CAMERA", 1, "2.2.3", "setPreviewCallback").unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.takePicture", "CAMERA", 1, "2.2.3", "takePicture").unsafe().doNotify()); + mListHook.add(new Hook("media", "MediaRecorder.start", "RECORD_AUDIO,RECORD_VIDEO", 1, "2.2.3", "setOutputFile").unsafe().doNotify()); + mListHook.add(new Hook("media", "MediaRecorder.setOutputFile", "RECORD_AUDIO,RECORD_VIDEO", 1, "2.99.20", "setOutputFile").unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera.permission", "CAMERA", 1, "2.2.3", null).dangerous().doNotify()); + mListHook.add(new Hook("media", "Record.Audio.permission", "RECORD_AUDIO", 3, "2.2.3", null).dangerous().doNotify()); + mListHook.add(new Hook("media", "Record.Video.permission", "RECORD_VIDEO", 3, "2.2.3", null).dangerous().doNotify()); + + mListHook.add(new Hook("media", MediaStore.ACTION_IMAGE_CAPTURE, "", 3, null, null).doNotify()); + mListHook.add(new Hook("media", MediaStore.ACTION_IMAGE_CAPTURE_SECURE, "", 17, null, null).doNotify()); + mListHook.add(new Hook("media", MediaStore.ACTION_VIDEO_CAPTURE, "", 3, null, null).doNotify()); + + mListHook.add(new Hook("media", "Camera2.capture", "CAMERA", 20, null, null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera2.captureBurst", "CAMERA", 20, null, null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera2.setRepeatingRequest", "CAMERA", 20, null, null).unsafe().doNotify()); + mListHook.add(new Hook("media", "Camera2.setRepeatingBurst", "CAMERA", 20, null, null).unsafe().doNotify()); + + mListHook.add(new Hook("messages", "getAllMessagesFromIcc", "RECEIVE_SMS", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("messages", "getCarrierConfigValues", "", 21, "3.5.6", null)); + mListHook.add(new Hook("messages", "Srv_getAllMessagesFromIccEf", "RECEIVE_SMS", 19, "2.99", "getAllMessagesFromIcc").AOSP(19)); + + mListHook.add(new Hook("messages", "SmsProvider", "READ_SMS,WRITE_SMS", 1, null, null)); + mListHook.add(new Hook("messages", "MmsProvider", "READ_SMS,WRITE_SMS", 1, null, null)); + mListHook.add(new Hook("messages", "MmsSmsProvider", "READ_SMS,WRITE_SMS", 1, null, null)); + mListHook.add(new Hook("messages", "VoicemailContentProvider", "com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL", 1, null, null)); + + mListHook.add(new Hook("messages", Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION, "RECEIVE_SMS", 1, null, null)); + mListHook.add(new Hook("messages", Telephony.Sms.Intents.SMS_RECEIVED_ACTION, "RECEIVE_SMS", 1, null, null)); + mListHook.add(new Hook("messages", Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION, "RECEIVE_WAP_PUSH", 1, null, null)); + mListHook.add(new Hook("messages", Telephony.Sms.Intents.SMS_DELIVER_ACTION, "BROADCAST_SMS", 19, "2.2.2", null)); + mListHook.add(new Hook("messages", Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION, "BROADCAST_WAP_PUSH", 19, "2.2.2", null)); + + // android.bluetooth.BluetoothAdapter/BluetoothDevice + mListHook.add(new Hook("network", "Bluetooth.getAddress", "BLUETOOTH", 5, "2.2.3", "getAddress").unsafe()); + mListHook.add(new Hook("network", "Bluetooth.getBondedDevices", "BLUETOOTH", 5, "2.2.3", "getBondedDevices").unsafe()); + mListHook.add(new Hook("network", "Bluetooth.Srv_getAddress", "BLUETOOTH", 5, "2.99", "getAddress").AOSP(19)); + mListHook.add(new Hook("network", "Bluetooth.Srv_getName", "BLUETOOTH", 5, "2.99", null).AOSP(19)); + + // java.net.NetworkInterface + mListHook.add(new Hook("network", "NetworkInterface.getHardwareAddress", "ACCESS_NETWORK_STATE", 9, "2.2.2", "getHardwareAddress").unsafe()); + mListHook.add(new Hook("network", "NetworkInterface.getInetAddresses", "ACCESS_NETWORK_STATE", 9, "2.2.2", "getInetAddresses").unsafe()); + mListHook.add(new Hook("network", "NetworkInterface.getInterfaceAddresses", "ACCESS_NETWORK_STATE", 9, "2.2.2", "getInterfaceAddresses").unsafe()); + + // android.net.wifi.WifiManager + mListHook.add(new Hook("network", "WiFi.getConfiguredNetworks", "ACCESS_WIFI_STATE", 10, "2.2.2", "getConfiguredNetworks").notAOSP(19)); + mListHook.add(new Hook("network", "WiFi.getConnectionInfo", "ACCESS_WIFI_STATE", 10, "2.2.2", "getConnectionInfo").notAOSP(19)); + mListHook.add(new Hook("network", "WiFi.getDhcpInfo", "ACCESS_WIFI_STATE", 10, "2.2.2", "getDhcpInfo").notAOSP(19)); + mListHook.add(new Hook("network", "WiFi.getScanResults", "ACCESS_WIFI_STATE", 10, "2.2.2", "getScanResults").notAOSP(19).dangerous()); + mListHook.add(new Hook("network", "WiFi.getWifiApConfiguration", "ACCESS_WIFI_STATE", 10, "2.2.2", "getWifiApConfiguration").notAOSP(19)); + + mListHook.add(new Hook("network", "WiFi.Srv_getBatchedScanResults", "ACCESS_WIFI_STATE", 10, "2.99", null).AOSP(19).dangerous()); + mListHook.add(new Hook("network", "WiFi.Srv_getConfiguredNetworks", "ACCESS_WIFI_STATE", 10, "2.99", "WiFi.getConfiguredNetworks").AOSP(19)); + mListHook.add(new Hook("network", "WiFi.Srv_getConnectionInfo", "ACCESS_WIFI_STATE", 10, "2.99", "WiFi.getConnectionInfo").AOSP(19)); + mListHook.add(new Hook("network", "WiFi.Srv_getDhcpInfo", "ACCESS_WIFI_STATE", 10, "2.99", "WiFi.getDhcpInfo").AOSP(19)); + mListHook.add(new Hook("network", "WiFi.Srv_getScanResults", "ACCESS_WIFI_STATE", 10, "2.99", "WiFi.getScanResults").AOSP(19).dangerous()); + mListHook.add(new Hook("network", "WiFi.Srv_getWifiApConfiguration", "ACCESS_WIFI_STATE", 10, "2.99", "WiFi.getWifiApConfiguration").AOSP(19)); + + mListHook.add(new Hook("network", "Srv_Default_DNS", "", 19, "2.99", "getString").AOSP(19).dangerous()); + mListHook.add(new Hook("network", "Srv_WiFi_Country", "", 19, "2.99", "getString").AOSP(19).dangerous()); + + // android.net.NetworkInfo + mListHook.add(new Hook("network", "NetworkInfo.getExtraInfo", null, 1, "2.2.2", "internet/getExtraInfo").unsafe()); + + mListHook.add(new Hook("nfc", "getNfcAdapter", "NFC", 14, null, null).unsafe()); + mListHook.add(new Hook("nfc", "getDefaultAdapter", "NFC", 10, null, null).unsafe()); + + mListHook.add(new Hook("nfc", NfcAdapter.ACTION_ADAPTER_STATE_CHANGED, "NFC", 18, null, null)); + mListHook.add(new Hook("nfc", NfcAdapter.ACTION_NDEF_DISCOVERED, "NFC", 10, null, null)); + mListHook.add(new Hook("nfc", NfcAdapter.ACTION_TAG_DISCOVERED, "NFC", 10, null, null)); + mListHook.add(new Hook("nfc", NfcAdapter.ACTION_TECH_DISCOVERED, "NFC", 10, null, null)); + + mListHook.add(new Hook("notifications", "android.service.notification.NotificationListenerService", "BIND_NOTIFICATION_LISTENER_SERVICE", 18, null, null).unsafe()); + mListHook.add(new Hook("notifications", "com.google.android.c2dm.intent.REGISTRATION", "com.google.android.c2dm.permission.RECEIVE", 10, null, null).dangerous()); + mListHook.add(new Hook("notifications", "com.google.android.c2dm.intent.RECEIVE", "com.google.android.c2dm.permission.RECEIVE", 10, null, null).dangerous()); + + mListHook.add(new Hook("overlay", "addView", "SYSTEM_ALERT_WINDOW", 1, null, null).unsafe().optional()); + + mListHook.add(new Hook("phone", "getDeviceId", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getGroupIdLevel1", "READ_PHONE_STATE", 18, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getIsimDomain", "READ_PRIVILEGED_PHONE_STATE", 14, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getIsimImpi", "READ_PRIVILEGED_PHONE_STATE", 14, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getIsimImpu", "READ_PRIVILEGED_PHONE_STATE", 14, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getLine1AlphaTag", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getLine1Number", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getMsisdn", "READ_PHONE_STATE", 14, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getSimSerialNumber", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getSubscriberId", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getVoiceMailAlphaTag", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "getVoiceMailNumber", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + + mListHook.add(new Hook("phone", "Srv_getDeviceId", "READ_PHONE_STATE", 10, "2.99", "getDeviceId").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getGroupIdLevel1", "READ_PHONE_STATE", 18, "2.99", "getGroupIdLevel1").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getIsimDomain", "READ_PRIVILEGED_PHONE_STATE", 14, "2.99", "getIsimDomain").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getIsimImpi", "READ_PRIVILEGED_PHONE_STATE", 14, "2.99", "getIsimImpi").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getIsimImpu", "READ_PRIVILEGED_PHONE_STATE", 14, "2.99", "getIsimImpu").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getLine1AlphaTag", "READ_PHONE_STATE", 10, "2.99", "getLine1AlphaTag").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getLine1Number", "READ_PHONE_STATE", 10, "2.99", "getLine1Number").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getMsisdn", "READ_PHONE_STATE", 14, "2.99", "getMsisdn").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getIccSerialNumber", "READ_PHONE_STATE", 10, "2.99", "getSimSerialNumber").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getSubscriberId", "READ_PHONE_STATE", 10, "2.99", "getSubscriberId").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getVoiceMailAlphaTag", "READ_PHONE_STATE", 10, "2.99", "getVoiceMailAlphaTag").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getVoiceMailNumber", "READ_PHONE_STATE", 10, "2.99", "getVoiceMailNumber").AOSP(19).to(20)); + mListHook.add(new Hook("phone", "Srv_getCompleteVoiceMailNumber", "READ_PHONE_STATE", 10, "2.99", null).AOSP(19).to(20)); + + mListHook.add(new Hook("phone", "Srv_getImei", "READ_PHONE_STATE", 21, "3.5.6", null).AOSP(21).obsolete()); + mListHook.add(new Hook("phone", "Srv_getIsimIst", "READ_PRIVILEGED_PHONE_STATE", 21, "3.5.6", null).AOSP(21).obsolete()); + mListHook.add(new Hook("phone", "Srv_getIsimPcscf", "READ_PRIVILEGED_PHONE_STATE", 21, "3.5.6", null).AOSP(21).obsolete()); + + mListHook.add(new Hook("phone", "Srv_getCdmaMdn", "MODIFY_PHONE_STATE", 21, "3.5.6", null).AOSP(21)); + mListHook.add(new Hook("phone", "Srv_getCdmaMin", "MODIFY_PHONE_STATE", 21, "3.5.6", null).AOSP(21)); + mListHook.add(new Hook("phone", "Srv_getLine1AlphaTagForDisplay", "READ_PHONE_STATE", 21, "3.5.6", null).AOSP(21).obsolete()); + mListHook.add(new Hook("phone", "Srv_getLine1NumberForDisplay", "READ_PHONE_STATE", 21, "3.5.6", null).AOSP(21).obsolete()); + + mListHook.add(new Hook("phone", "Srv_getCompleteVoiceMailNumberForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getCompleteVoiceMailNumber").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getDeviceId5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getDeviceId").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getDeviceIdForPhone5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getDeviceId").AOSP(Build.VERSION_CODES.LOLLIPOP_MR1)); + mListHook.add(new Hook("phone", "Srv_getDeviceIdForSubscriber5", "READ_PHONE_STATE", 21, "3.6.13", "Srv_getDeviceId").AOSP(Build.VERSION_CODES.LOLLIPOP).to(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getGroupIdLevel1ForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getGroupIdLevel1").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getIccSerialNumberForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getIccSerialNumber").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getImeiForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getImei").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getIsimDomain5", "READ_PRIVILEGED_PHONE_STATE", 21, "3.6.12", "Srv_getIsimDomain").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getIsimImpi5", "READ_PRIVILEGED_PHONE_STATE", 21, "3.6.12", "Srv_getIsimImpi").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getIsimImpu5", "READ_PRIVILEGED_PHONE_STATE", 21, "3.6.12", "Srv_getIsimImpu").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getIsimIst5", "READ_PRIVILEGED_PHONE_STATE", 21, "3.6.12", "Srv_getIsimIst").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getIsimPcscf5", "READ_PRIVILEGED_PHONE_STATE", 21, "3.6.12", "Srv_getIsimPcscf").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getLine1AlphaTagForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getLine1AlphaTagForDisplay").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getLine1NumberForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getLine1NumberForDisplay").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getMsisdnForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getMsisdn").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getNaiForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", null).AOSP(Build.VERSION_CODES.LOLLIPOP_MR1)); + mListHook.add(new Hook("phone", "Srv_getSubscriberIdForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getSubscriberId").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getVoiceMailAlphaTagForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getVoiceMailAlphaTag").AOSP(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook("phone", "Srv_getVoiceMailNumberForSubscriber5", "READ_PHONE_STATE", 21, "3.6.12", "Srv_getVoiceMailNumber").AOSP(Build.VERSION_CODES.LOLLIPOP)); + + mListHook.add(new Hook("phone", "listen", "READ_PHONE_STATE", 10, null, null).notAOSP(19)); + mListHook.add(new Hook("phone", "Srv_listen", "READ_PHONE_STATE", 10, null, null).AOSP(19)); + + mListHook.add(new Hook("phone", "getNetworkCountryIso", "", 10, null, null).unsafe()); + mListHook.add(new Hook("phone", "getNetworkOperator", "", 10, null, null).unsafe()); + mListHook.add(new Hook("phone", "getNetworkOperatorName", "", 10, null, null).unsafe()); + mListHook.add(new Hook("phone", "getSimCountryIso", "", 10, null, null).unsafe()); + mListHook.add(new Hook("phone", "getSimOperator", "", 10, null, null).unsafe()); + mListHook.add(new Hook("phone", "getSimOperatorName", "", 10, null, null).unsafe()); + + mListHook.add(new Hook("phone", TelephonyManager.ACTION_PHONE_STATE_CHANGED, "READ_PHONE_STATE", 10, null, null)); + mListHook.add(new Hook("phone", "TelephonyProvider", "WRITE_APN_SETTINGS", 1, null, null)); + + mListHook.add(new Hook("phone", "Configuration.MCC", "", 1, "2.0", null).unsafe().noUsageData().noOnDemand()); + mListHook.add(new Hook("phone", "Configuration.MNC", "", 1, "2.0", null).unsafe().noUsageData().noOnDemand()); + + mListHook.add(new Hook("sensors", "getDefaultSensor", "", 3, null, null).unsafe().dangerous()); + mListHook.add(new Hook("sensors", "getSensorList", "", 3, null, null).unsafe().dangerous()); + mListHook.add(new Hook("sensors", "registerListener", "", 3, "2.99.27", null).unsafe()); + mListHook.add(new Hook("sensors", "acceleration", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "gravity", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "humidity", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "light", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "magnetic", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "motion", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "orientation", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "pressure", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "proximity", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "rotation", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "temperature", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "step", "", 3, null, null).unsafe()); + mListHook.add(new Hook("sensors", "heartrate", "", 20, null, null).unsafe()); + + mListHook.add(new Hook("shell", "sh", "", 10, null, null).unsafe().dangerous().whitelist(cTypeCommand)); + mListHook.add(new Hook("shell", "su", "", 10, null, null).unsafe().dangerous().whitelist(cTypeCommand)); + mListHook.add(new Hook("shell", "exec", "", 10, null, null).unsafe().dangerous().whitelist(cTypeCommand)); + mListHook.add(new Hook("shell", "load", "", 10, null, null).unsafe().dangerous().restart().whitelist(cTypeLibrary)); + mListHook.add(new Hook("shell", "loadLibrary", "", 10, null, null).unsafe().dangerous().restart().whitelist(cTypeLibrary)); + mListHook.add(new Hook("shell", "start", "", 10, null, null).unsafe().dangerous().whitelist(cTypeCommand)); + + mListHook.add(new Hook("storage", "media", "WRITE_MEDIA_STORAGE", 10, null, null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("storage", "sdcard", "READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE,ACCESS_ALL_EXTERNAL_STORAGE", 10, null, null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("storage", "mtp", "ACCESS_MTP", 10, "2.1.1", null).dangerous().restart().noUsageData()); + mListHook.add(new Hook("storage", "getExternalStorageState", null, 10, null, null).unsafe().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "open", null, 1, "1.99.46", null).unsafe().dangerous().whitelist(cTypeFilename)); + + mListHook.add(new Hook("storage", "openAssetFileDescriptor", null, 3, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "openFileDescriptor", null, 1, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "openInputStream", null, 1, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "openOutputStream", null, 1, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "openTypedAssetFileDescriptor", null, 11, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "openAssetFile", null, 5, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + mListHook.add(new Hook("storage", "openFile", null, 5, "2.1.17", null).unsafe().dangerous().whitelist(cTypeFilename)); + + mListHook.add(new Hook("system", "getInstalledApplications", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getInstalledPackages", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getPackagesForUid", "", 1, "2.1.17", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getPackagesHoldingPermissions", "", 18, "1.99.1", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getPreferredActivities", "", 1, "1.99.44", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getPreferredPackages", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "queryBroadcastReceivers", "", 1, null, null).dangerous()); + mListHook.add(new Hook("system", "queryContentProviders", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "queryIntentActivities", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "queryIntentActivityOptions", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "queryIntentContentProviders", "", 19, "1.99.1", null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "queryIntentServices", "", 1, null, null).notAOSP(19).dangerous()); + + mListHook.add(new Hook("system", "Srv_getPackageInfo", "", 19, "2.99.30", null).AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getApplicationInfo", "", 19, "2.99.30", null).AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getInstalledApplications", "", 19, "2.99", "getInstalledApplications").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getInstalledPackages", "", 19, "2.99", "getInstalledPackages").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getPackagesForUid", "", 19, "2.99", "getPackagesForUid").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getPackagesHoldingPermissions", "", 19, "2.99", "getPackagesHoldingPermissions").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getPersistentApplications", "", 19, "2.99", null).AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getPreferredPackages", "", 19, "2.99", "getPreferredPackages").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_queryContentProviders", "", 19, "2.99", "queryContentProviders").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_queryIntentActivities", "", 19, "2.99", "queryIntentActivities").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_queryIntentActivityOptions", "", 19, "2.99", "queryIntentActivityOptions").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_queryIntentContentProviders", "", 19, "2.99", "queryIntentContentProviders").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_queryIntentReceivers", "", 19, "2.99", "queryBroadcastReceivers").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_queryIntentServices", "", 19, "2.99", "queryIntentServices").AOSP(19).dangerous()); + + mListHook.add(new Hook("system", "getInstalledProviders", "", 3, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getInstalledProvidersForProfile", "", 21, "3.5.6", null).notAOSP(21).dangerous()); + mListHook.add(new Hook("system", "Srv_getInstalledProviders", "", 3, "2.99", "getInstalledProviders").AOSP(19).to(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getInstalledProvidersForProfile", "", 3, "3.6.6", null).AOSP(21).dangerous()); + + mListHook.add(new Hook("system", "getRecentTasks", "GET_TASKS", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getRunningAppProcesses", "", 3, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getRunningServices", "", 1, null, null).notAOSP(19).dangerous()); + mListHook.add(new Hook("system", "getRunningTasks", "GET_TASKS", 1, null, null).notAOSP(19).dangerous()); + + mListHook.add(new Hook("system", "Srv_getRecentTasks", "GET_TASKS", 1, "2.99", "getRecentTasks").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getRunningAppProcesses", "", 3, "2.99", "getRunningAppProcesses").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getServices", "", 1, "2.99", "getRunningServices").AOSP(19).dangerous()); + mListHook.add(new Hook("system", "Srv_getTasks", "GET_TASKS", 1, "2.99", "getRunningTasks").AOSP(19).dangerous()); + + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_ADDED, "", 1, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_REPLACED, "", 3, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_RESTARTED, "", 1, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_REMOVED, "", 1, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_CHANGED, "", 1, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_DATA_CLEARED, "", 3, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_FIRST_LAUNCH, "", 12, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_FULLY_REMOVED, "", 14, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_NEEDS_VERIFICATION, "", 14, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_PACKAGE_VERIFIED, "", 17, "2.2.2", null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, "", 8, null, null).dangerous()); + mListHook.add(new Hook("system", Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE, "", 8, null, null).dangerous()); + + mListHook.add(new Hook("system", "ApplicationsProvider", "", 1, null, null).to(18)); + + mListHook.add(new Hook("system", "checkPermission", "", 1, "2.1.24", null).AOSP(19).dangerous().whitelist(cTypePermission)); + mListHook.add(new Hook("system", "checkUidPermission", "", 1, "2.1.24", null).AOSP(19).dangerous().whitelist(cTypePermission)); + + mListHook.add(new Hook("system", "IntentFirewall", "", 19, "2.2.2", null).AOSP(19).dangerous().whitelist(cTypeAction)); + + mListHook.add(new Hook("system", "queryAndAggregateUsageStats", null, 21, "3.5.6", null).notAOSP(21)); + mListHook.add(new Hook("system", "queryConfigurations", null, 21, "3.5.6", null).notAOSP(21)); + mListHook.add(new Hook("system", "queryEvents", null, 21, "3.5.6", null).notAOSP(21)); + mListHook.add(new Hook("system", "queryUsageStats", null, 21, "3.5.6", null).notAOSP(21)); + mListHook.add(new Hook("system", "Srv_queryConfigurationStats", null, 21, "3.5.6", null).AOSP(21)); + mListHook.add(new Hook("system", "Srv_queryEvents", null, 21, "3.5.6", null).AOSP(21)); + mListHook.add(new Hook("system", "Srv_queryUsageStats", null, 21, "3.5.6", null).AOSP(21)); + + mListHook.add(new Hook("view", "loadUrl", "", 1, "3.6.2", "false").unsafe().whitelist(cTypeUrl)); + mListHook.add(new Hook("view", "postUrl", "", 1, "3.6.2", null).unsafe().whitelist(cTypeUrl)); + mListHook.add(new Hook("view", "initUserAgentString", "", 3, "3.6.2", null).unsafe()); + mListHook.add(new Hook("view", "getDefaultUserAgent", "", 17, null, null).unsafe()); + mListHook.add(new Hook("view", "getUserAgent", "", 3, null, null).unsafe()); + mListHook.add(new Hook("view", "getUserAgentString", "", 3, null, null).unsafe()); + mListHook.add(new Hook("view", "setUserAgent", "", 3, null, null).unsafe()); + mListHook.add(new Hook("view", "setUserAgentString", "", 3, null, null).unsafe()); + + mListHook.add(new Hook("view", Intent.ACTION_VIEW, "", 1, null, null).notAOSP(19).doNotify().whitelist(cTypeUrl)); + mListHook.add(new Hook("view", "Srv_" + Intent.ACTION_VIEW, "", 19, "2.99", Intent.ACTION_VIEW).AOSP(19).doNotify().whitelist(cTypeUrl)); + + mListHook.add(new Hook("view", "GMS5.view", "", 1, "2.99.27", null).unsafe()); + + // AccountManager + mListHook.add(new Hook(null, "removeOnAccountsUpdatedListener", "", 5, null, null)); + + // Activity + mListHook.add(new Hook(null, "startActivities", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "startActivity", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "startActivityForResult", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "startActivityFromChild", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "startActivityFromFragment", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "startActivityIfNeeded", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "startNextMatchingActivity", "", 1, null, null).notAOSP(19)); + + // ActivityThread / MessageQueue + mListHook.add(new Hook(null, "next", "", 1, null, null).notAOSP(19).optional()); + mListHook.add(new Hook(null, "handleReceiver", "", 1, null, null).notAOSP(19).optional()); + + // ActivityManager(Service) + mListHook.add(new Hook(null, "Srv_startActivities", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_startActivity", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_startActivityAsUser", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_startActivityAsCaller", "", 21, null, null).AOSP(21)); + mListHook.add(new Hook(null, "Srv_startActivityAndWait", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_startActivityWithConfig", "", 19, null, null).AOSP(19)); + + mListHook.add(new Hook(null, "inputDispatchingTimedOut", "", 17, null, null)); + mListHook.add(new Hook(null, "appNotResponding", "", 15, null, null).optional()); + mListHook.add(new Hook(null, "systemReady", "", 15, null, null)); + mListHook.add(new Hook(null, "finishBooting", "", 15, null, null)); + mListHook.add(new Hook(null, "setLockScreenShown", "", 17, null, null).optional()); + mListHook.add(new Hook(null, "goingToSleep", "", 16, null, null).to(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook(null, "wakingUp", "", 16, null, null).to(Build.VERSION_CODES.LOLLIPOP)); + mListHook.add(new Hook(null, "updateSleepIfNeededLocked", "", Build.VERSION_CODES.LOLLIPOP_MR1, null, null)); + mListHook.add(new Hook(null, "shutdown", "", 15, null, null)); + mListHook.add(new Hook(null, "activityResumed", "", Build.VERSION_CODES.JELLY_BEAN_MR1, null, null)); + mListHook.add(new Hook(null, "activityPaused", "", Build.VERSION_CODES.JELLY_BEAN_MR1, null, null)); + + // AppIndexApi + mListHook.add(new Hook(null, "GMS5.viewEnd", "", 1, null, null)); + + // Application + mListHook.add(new Hook(null, "onCreate", "", 1, null, null)); + + // AudioRecord + mListHook.add(new Hook(null, "Audio.stop", "", 3, null, null)); + + // Binder + mListHook.add(new Hook(null, "execTransact", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "transact", "", 1, null, null).notAOSP(19)); + + // ClipboardManager/Service + mListHook.add(new Hook(null, "removePrimaryClipChangedListener", "", 11, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "Srv_removePrimaryClipChangedListener", "", 11, null, null).AOSP(19)); + + // Content resolvers + mListHook.add(new Hook(null, "query", "", 1, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "Srv_call", "", 1, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_query", "", 1, null, null).AOSP(19)); + + // Camera + mListHook.add(new Hook(null, "Camera.stopPreview", "", 1, null, null)); + + // ContextImpl + mListHook.add(new Hook(null, "getPackageManager", "", 1, null, null).notAOSP(19)); + + // ContextImpl / Activity + mListHook.add(new Hook(null, "getSystemService", "", 1, null, null).notAOSP(19)); + + // FusedLocationProviderApi // ActivityRecognitionApi + mListHook.add(new Hook(null, "GMS5.removeLocationUpdates", "", 1, "2.99.26", null).optional()); + mListHook.add(new Hook(null, "GMS5.removeActivityUpdates", "", 1, "2.99.26", null).optional()); + + // GoogleApiClient.Builder + mListHook.add(new Hook(null, "GMS5.addConnectionCallbacks", "", 1, null, null).optional()); + mListHook.add(new Hook(null, "GMS5.onConnected", "", 1, null, null)); + + // IntentFirewall + mListHook.add(new Hook(null, "checkIntent", "", 19, null, null)); + + // LocationClient / ActivityRecognitionClient + mListHook.add(new Hook(null, "GMS.removeActivityUpdates", "", 1, null, null)); + mListHook.add(new Hook(null, "GMS.removeGeofences", "", 1, null, null).optional()); + mListHook.add(new Hook(null, "GMS.removeLocationUpdates", "", 1, null, null).optional()); + + // LocationManager/Service + mListHook.add(new Hook(null, "removeUpdates", "", 3, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "Srv_removeUpdates", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_removeGeofence", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_removeGpsStatusListener", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_removeGpsMeasurementsListener", "", 21, null, null).AOSP(21)); + mListHook.add(new Hook(null, "Srv_removeGpsNavigationMessageListener", "", 21, null, null).AOSP(21)); + mListHook.add(new Hook(null, "MapV1.disableMyLocation", "", 1, null, null).optional()); + + // MediaRecorder + mListHook.add(new Hook(null, "MediaRecorder.prepare", "", 1, null, null)); + mListHook.add(new Hook(null, "MediaRecorder.stop", "", 1, null, null)); + + // Resources + mListHook.add(new Hook(null, "updateConfiguration", "", 1, null, null)); + + // TelephonyManager + mListHook.add(new Hook(null, "disableLocationUpdates", "", 10, null, null).notAOSP(19)); + mListHook.add(new Hook(null, "Srv_disableLocationUpdates", "", 19, null, null).AOSP(19)); + mListHook.add(new Hook(null, "Srv_disableLocationUpdatesForSubscriber", "", 21, null, null).AOSP(21)); + + // UtilHook + mListHook.add(new Hook(null, "isXposedEnabled", "", 15, null, null)); + + // WebView + mListHook.add(new Hook(null, "WebView", "", 1, null, null)); + mListHook.add(new Hook(null, "getSettings", "", 1, null, null)); + + // WindowManagerImpl + mListHook.add(new Hook(null, "removeView", "", 1, null, null).optional()); + mListHook.add(new Hook(null, "updateViewLayout", "", 1, null, null).optional()); + + // @formatter:on + return mListHook; + } + + public static void annotate(Resources resources) { + if (mAnnotated) + return; + + String self = Meta.class.getPackage().getName(); + for (Hook hook : get()) + if (hook.getRestrictionName() != null) { + String name = hook.getRestrictionName() + "_" + hook.getName(); + name = name.replace(".", "_").replace("/", "_").replace("%", "_").replace("-", "_"); + int resId = resources.getIdentifier(name, "string", self); + if (resId > 0) + hook.annotate(resources.getString(resId)); + else + Util.log(null, Log.WARN, "Missing annotation hook=" + hook); + } + + mAnnotated = true; + } +} \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PRestriction.aidl b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PRestriction.aidl new file mode 100644 index 0000000..16f52ae --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PRestriction.aidl @@ -0,0 +1,3 @@ +package biz.bokhorst.xprivacy; + +parcelable PRestriction; diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PRestriction.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PRestriction.java new file mode 100644 index 0000000..ff257a7 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PRestriction.java @@ -0,0 +1,135 @@ +package biz.bokhorst.xprivacy; + +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; + +public class PRestriction implements Parcelable { + public int uid; + public String restrictionName; + public String methodName; + public boolean restricted; + public boolean asked; + public String extra; + public String value; + public long time; + public boolean debug; + + // The extra is never needed in the result + + public PRestriction() { + } + + public PRestriction(PRestriction other) { + uid = other.uid; + restrictionName = other.restrictionName; + methodName = other.methodName; + restricted = other.restricted; + asked = other.asked; + extra = null; + value = other.value; + time = other.time; + debug = other.debug; + } + + public PRestriction(int _uid, String category, String method) { + uid = _uid; + restrictionName = category; + methodName = method; + restricted = false; + asked = false; + extra = null; + value = null; + time = 0; + debug = false; + } + + public PRestriction(int _uid, String category, String method, boolean _restricted) { + uid = _uid; + restrictionName = category; + methodName = method; + restricted = _restricted; + asked = false; + extra = null; + value = null; + time = 0; + debug = false; + } + + public PRestriction(int _uid, String category, String method, boolean _restricted, boolean _asked) { + uid = _uid; + restrictionName = category; + methodName = method; + restricted = _restricted; + asked = _asked; + extra = null; + value = null; + time = 0; + debug = false; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public PRestriction createFromParcel(Parcel in) { + return new PRestriction(in); + } + + public PRestriction[] newArray(int size) { + return new PRestriction[size]; + } + }; + + private PRestriction(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(uid); + + out.writeInt(restrictionName == null ? 1 : 0); + if (restrictionName != null) + out.writeString(restrictionName); + + out.writeInt(methodName == null ? 1 : 0); + if (methodName != null) + out.writeString(methodName); + + out.writeInt(restricted ? 1 : 0); + out.writeInt(asked ? 1 : 0); + + out.writeInt(extra == null ? 1 : 0); + if (extra != null) + out.writeString(extra); + + out.writeInt(value == null ? 1 : 0); + if (value != null) + out.writeString(value); + + out.writeLong(time); + out.writeInt(debug ? 1 : 0); + } + + public void readFromParcel(Parcel in) { + uid = in.readInt(); + restrictionName = (in.readInt() > 0 ? null : in.readString()); + methodName = (in.readInt() > 0 ? null : in.readString()); + restricted = (in.readInt() > 0 ? true : false); + asked = (in.readInt() > 0 ? true : false); + extra = (in.readInt() > 0 ? null : in.readString()); + value = (in.readInt() > 0 ? null : in.readString()); + time = in.readLong(); + debug = (in.readInt() > 0 ? true : false); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + @SuppressLint("DefaultLocale") + public String toString() { + return String.format("%d/%s(%s;%s) %s=%srestricted%s", uid, methodName, extra, value, restrictionName, + (restricted ? "" : "!"), (asked ? "" : "?")); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PSetting.aidl b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PSetting.aidl new file mode 100644 index 0000000..7537240 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PSetting.aidl @@ -0,0 +1,3 @@ +package biz.bokhorst.xprivacy; + +parcelable PSetting; diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PSetting.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PSetting.java new file mode 100644 index 0000000..80b7ef5 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PSetting.java @@ -0,0 +1,73 @@ +package biz.bokhorst.xprivacy; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PSetting implements Parcelable { + public int uid; + public String type; + public String name; + public String value; + + public PSetting() { + } + + public PSetting(PSetting other) { + uid = other.uid; + type = other.type; + name = other.name; + value = other.value; + } + + public PSetting(int _uid, String _type, String _name, String _value) { + uid = _uid; + type = _type; + name = _name; + value = _value; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public PSetting createFromParcel(Parcel in) { + return new PSetting(in); + } + + public PSetting[] newArray(int size) { + return new PSetting[size]; + } + }; + + private PSetting(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(uid); + out.writeInt(type == null ? 1 : 0); + if (type != null) + out.writeString(type); + out.writeInt(name == null ? 1 : 0); + if (name != null) + out.writeString(name); + out.writeInt(value == null ? 1 : 0); + if (value != null) + out.writeString(value); + } + + public void readFromParcel(Parcel in) { + uid = in.readInt(); + type = (in.readInt() > 0 ? null : in.readString()); + name = (in.readInt() > 0 ? null : in.readString()); + value = (in.readInt() > 0 ? null : in.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "uid=" + uid + " " + type + "/" + name + "=" + (value == null ? "null" : value); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PackageChange.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PackageChange.java new file mode 100644 index 0000000..d7ebdb5 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PackageChange.java @@ -0,0 +1,175 @@ +package biz.bokhorst.xprivacy; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +public class PackageChange extends BroadcastReceiver { + @Override + public void onReceive(final Context context, Intent intent) { + try { + // Check uri + Uri inputUri = intent.getData(); + if (inputUri.getScheme().equals("package")) { + // Get data + int uid = intent.getIntExtra(Intent.EXTRA_UID, 0); + int userId = Util.getUserId(uid); + boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + boolean ondemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + NotificationManager notificationManager = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + + Util.log(null, Log.WARN, "Package change action=" + intent.getAction() + " replacing=" + replacing + + " uid=" + uid); + + // Check action + if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) { + // Check privacy service + if (PrivacyService.getClient() == null) + return; + + // Get data + ApplicationInfoEx appInfo = new ApplicationInfoEx(context, uid); + String packageName = inputUri.getSchemeSpecificPart(); + + // Default deny new user apps + if (appInfo.getPackageName().size() == 1) { + if (replacing) + PrivacyManager.clearPermissionCache(uid); + else { + // Delete existing restrictions + PrivacyManager.deleteRestrictions(uid, null, true); + PrivacyManager.deleteSettings(uid); + PrivacyManager.deleteUsage(uid); + PrivacyManager.clearPermissionCache(uid); + + // Apply template + PrivacyManager.applyTemplate(uid, Meta.cTypeTemplate, null, true, true, false); + + // Enable on demand + if (ondemand) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOnDemand, Boolean.toString(true)); + } + } + + // Mark as new/changed + PrivacyManager.setSetting(uid, PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_ATTENTION)); + + // New/update notification + boolean notify = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingNotify, true); + if (notify) + notify = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingNotify, true); + if (!replacing || notify) { + Intent resultIntent = new Intent(context, ActivityApp.class); + resultIntent.putExtra(ActivityApp.cUid, uid); + + // Build pending intent + PendingIntent pendingIntent = PendingIntent.getActivity(context, uid, resultIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + // Build result intent settings + Intent resultIntentSettings = new Intent(context, ActivityApp.class); + resultIntentSettings.putExtra(ActivityApp.cUid, uid); + resultIntentSettings.putExtra(ActivityApp.cAction, ActivityApp.cActionSettings); + + // Build pending intent settings + PendingIntent pendingIntentSettings = PendingIntent.getActivity(context, uid - 10000, + resultIntentSettings, PendingIntent.FLAG_UPDATE_CURRENT); + + // Build result intent clear + Intent resultIntentClear = new Intent(context, ActivityApp.class); + resultIntentClear.putExtra(ActivityApp.cUid, uid); + resultIntentClear.putExtra(ActivityApp.cAction, ActivityApp.cActionClear); + + // Build pending intent clear + PendingIntent pendingIntentClear = PendingIntent.getActivity(context, uid + 10000, + resultIntentClear, PendingIntent.FLAG_UPDATE_CURRENT); + + // Title + String title = String.format("%s %s %s", + context.getString(replacing ? R.string.msg_update : R.string.msg_new), + appInfo.getApplicationName(packageName), + appInfo.getPackageVersionName(context, packageName)); + if (!replacing) + title = String.format("%s %s", title, context.getString(R.string.msg_applied)); + + // Build notification + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context); + notificationBuilder.setSmallIcon(R.drawable.ic_launcher); + notificationBuilder.setContentTitle(context.getString(R.string.app_name)); + notificationBuilder.setContentText(title); + notificationBuilder.setContentIntent(pendingIntent); + notificationBuilder.setWhen(System.currentTimeMillis()); + notificationBuilder.setAutoCancel(true); + + // Actions + notificationBuilder.addAction(android.R.drawable.ic_menu_edit, + context.getString(R.string.menu_app_settings), pendingIntentSettings); + notificationBuilder.addAction(android.R.drawable.ic_menu_delete, + context.getString(R.string.menu_clear), pendingIntentClear); + + // Notify + Notification notification = notificationBuilder.build(); + notificationManager.notify(appInfo.getUid(), notification); + } + + } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) { + // Check privacy service + if (PrivacyService.getClient() == null) + return; + + if (!replacing) { + // Package removed + notificationManager.cancel(uid); + + // Delete restrictions + ApplicationInfoEx appInfo = new ApplicationInfoEx(context, uid); + if (appInfo.getPackageName().size() == 0) { + PrivacyManager.deleteRestrictions(uid, null, false); + PrivacyManager.deleteSettings(uid); + PrivacyManager.deleteUsage(uid); + PrivacyManager.clearPermissionCache(uid); + } + } + + } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) { + // Notify reboot required + String packageName = inputUri.getSchemeSpecificPart(); + if (packageName.equals(context.getPackageName())) { + // Mark self as new/changed + if (PrivacyService.getClient() != null) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_ATTENTION)); + + // Start package update + Intent changeIntent = new Intent(); + changeIntent.setClass(context, UpdateService.class); + changeIntent.putExtra(UpdateService.cAction, UpdateService.cActionUpdated); + context.startService(changeIntent); + + // Build notification + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context); + notificationBuilder.setSmallIcon(R.drawable.ic_launcher); + notificationBuilder.setContentTitle(context.getString(R.string.app_name)); + notificationBuilder.setContentText(context.getString(R.string.msg_reboot)); + notificationBuilder.setWhen(System.currentTimeMillis()); + notificationBuilder.setAutoCancel(true); + Notification notification = notificationBuilder.build(); + + // Notify + notificationManager.notify(Util.NOTIFY_RESTART, notification); + } + } + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyManager.java new file mode 100644 index 0000000..b6a1fac --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyManager.java @@ -0,0 +1,1419 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.location.Location; +import android.os.Build; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; +import android.util.SparseArray; + +public class PrivacyManager { + public static final boolean cVersion3 = true; + + // This should correspond with restrict_ in strings.xml + public static final String cAccounts = "accounts"; + public static final String cBrowser = "browser"; + public static final String cCalendar = "calendar"; + public static final String cCalling = "calling"; + public static final String cClipboard = "clipboard"; + public static final String cContacts = "contacts"; + public static final String cDictionary = "dictionary"; + public static final String cEMail = "email"; + public static final String cIdentification = "identification"; + public static final String cInternet = "internet"; + public static final String cIPC = "ipc"; + public static final String cLocation = "location"; + public static final String cMedia = "media"; + public static final String cMessages = "messages"; + public static final String cNetwork = "network"; + public static final String cNfc = "nfc"; + public static final String cNotifications = "notifications"; + public static final String cOverlay = "overlay"; + public static final String cPhone = "phone"; + public static final String cSensors = "sensors"; + public static final String cShell = "shell"; + public static final String cStorage = "storage"; + public static final String cSystem = "system"; + public static final String cView = "view"; + + // This should correspond with the above definitions + private static final String cRestrictionNames[] = new String[] { cAccounts, cBrowser, cCalendar, cCalling, + cClipboard, cContacts, cDictionary, cEMail, cIdentification, cInternet, cIPC, cLocation, cMedia, cMessages, + cNetwork, cNfc, cNotifications, cOverlay, cPhone, cSensors, cShell, cStorage, cSystem, cView }; + + public static List cMethodNoState = Arrays.asList(new String[] { "IntentFirewall", "checkPermission", + "checkUidPermission" }); + + // Setting names + public final static String cSettingSerial = "Serial"; + public final static String cSettingLatitude = "Latitude"; + public final static String cSettingLongitude = "Longitude"; + public final static String cSettingAltitude = "Altitude"; + public final static String cSettingMac = "Mac"; + public final static String cSettingIP = "IP"; + public final static String cSettingImei = "IMEI"; + public final static String cSettingPhone = "Phone"; + public final static String cSettingId = "ID"; + public final static String cSettingGsfId = "GSF_ID"; + public final static String cSettingAdId = "AdId"; + public final static String cSettingMcc = "MCC"; + public final static String cSettingMnc = "MNC"; + public final static String cSettingCountry = "Country"; + public final static String cSettingOperator = "Operator"; + public final static String cSettingIccId = "ICC_ID"; + public final static String cSettingSubscriber = "Subscriber"; + public final static String cSettingSSID = "SSID"; + public final static String cSettingUa = "UA"; + public final static String cSettingOpenTab = "OpenTab"; + public final static String cSettingSelectedCategory = "SelectedCategory"; + public final static String cSettingFUsed = "FUsed"; + public final static String cSettingFInternet = "FInternet"; + public final static String cSettingFRestriction = "FRestriction"; + public final static String cSettingFRestrictionNot = "FRestrictionNot"; + public final static String cSettingFPermission = "FPermission"; + public final static String cSettingFOnDemand = "FOnDemand"; + public final static String cSettingFOnDemandNot = "FOnDemandNot"; + public final static String cSettingFUser = "FUser"; + public final static String cSettingFSystem = "FSystem"; + public final static String cSettingSortMode = "SortMode"; + public final static String cSettingSortInverted = "SortInverted"; + public final static String cSettingModifyTime = "ModifyTime"; + public final static String cSettingTheme = "Theme"; + public final static String cSettingSalt = "Salt"; + public final static String cSettingVersion = "Version"; + public final static String cSettingFirstRun = "FirstRun"; + public final static String cSettingTutorialMain = "TutorialMain"; + public final static String cSettingTutorialDetails = "TutorialDetails"; + public final static String cSettingNotify = "Notify"; + public final static String cSettingLog = "Log"; + public final static String cSettingDangerous = "Dangerous"; + public final static String cSettingExperimental = "Experimental"; + public final static String cSettingRandom = "Random@boot"; + public final static String cSettingState = "State"; + public final static String cSettingConfidence = "Confidence"; + public final static String cSettingHttps = "Https"; + public final static String cSettingRegistered = "Registered"; + public final static String cSettingUsage = "UsageData"; + public final static String cSettingParameters = "Parameters"; + public final static String cSettingValues = "Values"; + public final static String cSettingSystem = "RestrictSystem"; + public final static String cSettingRestricted = "Retricted"; + public final static String cSettingOnDemand = "OnDemand"; + public final static String cSettingMigrated = "Migrated"; + public final static String cSettingCid = "Cid"; + public final static String cSettingLac = "Lac"; + public final static String cSettingBlacklist = "Blacklist"; + public final static String cSettingResolve = "Resolve"; + public final static String cSettingNoResolve = "NoResolve"; + public final static String cSettingFreeze = "Freeze"; + public final static String cSettingPermMan = "PermMan"; + public final static String cSettingIntentWall = "IntentWall"; + public final static String cSettingSafeMode = "SafeMode"; + public final static String cSettingTestVersions = "TestVersions"; + public final static String cSettingOnDemandSystem = "OnDemandSystem"; + public final static String cSettingLegacy = "Legacy"; + public final static String cSettingAOSPMode = "AOSPMode"; + public final static String cSettingChangelog = "Changelog"; + public final static String cSettingUpdates = "Updates"; + public final static String cSettingMethodExpert = "MethodExpert"; + public final static String cSettingWhitelistNoModify = "WhitelistNoModify"; + public final static String cSettingNoUsageData = "NoUsageData"; + + public final static String cSettingODExpert = "ODExpert"; + public final static String cSettingODCategory = "ODCategory"; + public final static String cSettingODOnce = "ODOnce"; + public final static String cSettingODOnceDuration = "ODOnceDuration"; + + // Special value names + public final static String cValueRandom = "#Random#"; + public final static String cValueRandomLegacy = "\nRandom\n"; + + // Constants + public final static int cXposedAppProcessMinVersion = 46; + public final static int cWarnServiceDelayMs = 200; + public final static int cWarnHookDelayMs = 200; + + private final static int cMaxExtra = 128; + private final static String cDeface = "DEFACE"; + + // Caching + public final static int cRestrictionCacheTimeoutMs = 15 * 1000; + public final static int cSettingCacheTimeoutMs = 30 * 1000; + private static Map> mMethod = new LinkedHashMap>(); + private static Map> mRestart = new LinkedHashMap>(); + private static Map> mPermission = new LinkedHashMap>(); + private static Map mSettingsCache = new HashMap(); + private static Map mTransientCache = new HashMap(); + private static Map mRestrictionCache = new HashMap(); + private static SparseArray> mPermissionRestrictionCache = new SparseArray>(); + private static SparseArray> mPermissionHookCache = new SparseArray>(); + + // Meta data + + static { + List listHook = Meta.get(); + List listRestriction = getRestrictions(); + for (Hook hook : listHook) { + String restrictionName = hook.getRestrictionName(); + if (restrictionName == null) + restrictionName = ""; + + // Check restriction + else if (!listRestriction.contains(restrictionName)) + if (hook.isAvailable()) + Util.log(null, Log.WARN, "Not found restriction=" + restrictionName + " hook=" + hook); + + // Enlist method + if (!mMethod.containsKey(restrictionName)) + mMethod.put(restrictionName, new HashMap()); + mMethod.get(restrictionName).put(hook.getName(), hook); + + // Cache restart required methods + if (hook.isRestartRequired()) { + if (!mRestart.containsKey(restrictionName)) + mRestart.put(restrictionName, new ArrayList()); + mRestart.get(restrictionName).add(hook.getName()); + } + + // Enlist permissions + String[] permissions = hook.getPermissions(); + if (permissions != null) + for (String perm : permissions) + if (!perm.equals("")) { + String aPermission = (perm.contains(".") ? perm : "android.permission." + perm); + if (!mPermission.containsKey(aPermission)) + mPermission.put(aPermission, new ArrayList()); + if (!mPermission.get(aPermission).contains(hook)) + mPermission.get(aPermission).add(hook); + } + } + // Util.log(null, Log.WARN, listHook.size() + " hooks"); + } + + public static List getRestrictions() { + List listRestriction = new ArrayList(Arrays.asList(cRestrictionNames)); + if (Hook.isAOSP(19)) + listRestriction.remove(cIPC); + return listRestriction; + } + + public static TreeMap getRestrictions(Context context) { + Collator collator = Collator.getInstance(Locale.getDefault()); + TreeMap tmRestriction = new TreeMap(collator); + for (String restrictionName : getRestrictions()) { + int stringId = context.getResources().getIdentifier("restrict_" + restrictionName, "string", + context.getPackageName()); + tmRestriction.put(stringId == 0 ? restrictionName : context.getString(stringId), restrictionName); + } + return tmRestriction; + } + + public static Hook getHook(String _restrictionName, String methodName) { + String restrictionName = (_restrictionName == null ? "" : _restrictionName); + if (mMethod.containsKey(restrictionName)) + if (mMethod.get(restrictionName).containsKey(methodName)) + return mMethod.get(restrictionName).get(methodName); + return null; + } + + public static List getHooks(String restrictionName, Version version) { + List listMethod = new ArrayList(); + for (String methodName : mMethod.get(restrictionName).keySet()) { + Hook hook = mMethod.get(restrictionName).get(methodName); + + if (!hook.isAvailable()) + continue; + + if (version != null && hook.getFrom() != null && version.compareTo(hook.getFrom()) < 0) + continue; + + if ("IntentFirewall".equals(hook.getName())) + if (!PrivacyManager.getSettingBool(0, PrivacyManager.cSettingIntentWall, false)) + continue; + + if ("checkPermission".equals(hook.getName()) || "checkUidPermission".equals(hook.getName())) + if (!PrivacyManager.getSettingBool(0, PrivacyManager.cSettingPermMan, false)) + continue; + + listMethod.add(mMethod.get(restrictionName).get(methodName)); + } + Collections.sort(listMethod); + return listMethod; + } + + public static List getPermissions(String restrictionName, Version version) { + List listPermission = new ArrayList(); + for (Hook md : getHooks(restrictionName, version)) + if (md.getPermissions() != null) + for (String permission : md.getPermissions()) + if (!listPermission.contains(permission)) + listPermission.add(permission); + return listPermission; + } + + // Restrictions + + public static PRestriction getRestrictionEx(int uid, String restrictionName, String methodName) { + PRestriction query = new PRestriction(uid, restrictionName, methodName, false); + PRestriction result = new PRestriction(uid, restrictionName, methodName, false, true); + try { + // Check cache + boolean cached = false; + CRestriction key = new CRestriction(uid, restrictionName, methodName, null); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(key)) { + CRestriction entry = mRestrictionCache.get(key); + if (!entry.isExpired()) { + cached = true; + result.restricted = entry.restricted; + result.asked = entry.asked; + } + } + } + + if (!cached) { + // Get restriction + result = PrivacyService.getRestrictionProxy(query, false, ""); + if (result.debug) + Util.logStack(null, Log.WARN); + + // Add to cache + key.restricted = result.restricted; + key.asked = result.asked; + if (result.time > 0) { + key.setExpiry(result.time); + Util.log(null, Log.WARN, "Caching " + result + " until " + new Date(result.time)); + } + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(key)) + mRestrictionCache.remove(key); + mRestrictionCache.put(key, key); + } + } + } catch (RemoteException ex) { + Util.bug(null, ex); + } + return result; + } + + public static boolean getRestriction(final XHook hook, int uid, String restrictionName, String methodName, + String secret) { + return getRestrictionExtra(hook, uid, restrictionName, methodName, null, null, secret); + } + + public static boolean getRestrictionExtra(final XHook hook, int uid, String restrictionName, String methodName, + String extra, String secret) { + return getRestrictionExtra(hook, uid, restrictionName, methodName, extra, null, secret); + } + + public static boolean getRestrictionExtra(final XHook hook, int uid, String restrictionName, String methodName, + String extra, String value, String secret) { + long start = System.currentTimeMillis(); + PRestriction result = new PRestriction(uid, restrictionName, methodName, false, true); + + // Check uid + if (uid <= 0) + return false; + + // Check secret + if (secret == null) { + Util.log(null, Log.ERROR, "Secret missing restriction=" + restrictionName + "/" + methodName); + Util.logStack(hook, Log.ERROR); + secret = ""; + } + + // Check restriction + if (restrictionName == null || restrictionName.equals("")) { + Util.log(hook, Log.ERROR, "restriction empty method=" + methodName); + Util.logStack(hook, Log.ERROR); + return false; + } + + // Check usage + if (methodName == null || methodName.equals("")) { + Util.log(hook, Log.ERROR, "Method empty"); + Util.logStack(hook, Log.ERROR); + } else if (getHook(restrictionName, methodName) == null) { + Util.log(hook, Log.ERROR, "Unknown method=" + methodName); + Util.logStack(hook, Log.ERROR); + } + + // Check extra + if (extra != null && extra.length() > cMaxExtra) + extra = extra.substring(0, cMaxExtra) + "..."; + result.extra = extra; + + // Check cache + boolean cached = false; + CRestriction key = new CRestriction(uid, restrictionName, methodName, extra); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(key)) { + CRestriction entry = mRestrictionCache.get(key); + if (!entry.isExpired()) { + cached = true; + result.restricted = entry.restricted; + result.asked = entry.asked; + } + } + } + + // Get restriction + if (!cached) + try { + PRestriction query = new PRestriction(uid, restrictionName, methodName, false); + query.extra = extra; + query.value = value; + PRestriction restriction = PrivacyService.getRestrictionProxy(query, true, secret); + result.restricted = restriction.restricted; + if (restriction.debug) + Util.logStack(null, Log.WARN); + + // Add to cache + if (result.time >= 0) { + key.restricted = result.restricted; + key.asked = result.asked; + if (result.time > 0) { + key.setExpiry(result.time); + Util.log(null, Log.WARN, "Caching " + result + " until " + new Date(result.time)); + } + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(key)) + mRestrictionCache.remove(key); + mRestrictionCache.put(key, key); + } + } + } catch (Throwable ex) { + Util.bug(hook, ex); + } + + // Result + long ms = System.currentTimeMillis() - start; + Util.log(hook, ms < cWarnServiceDelayMs ? Log.INFO : Log.WARN, + String.format("Get client %s%s %d ms", result, (cached ? " (cached)" : ""), ms)); + + return result.restricted; + } + + public static void setRestriction(int uid, String restrictionName, String methodName, boolean restricted, + boolean asked) { + checkCaller(); + + // Check uid + if (uid == 0) { + Util.log(null, Log.WARN, "uid=0"); + return; + } + + // Build list of restrictions + List listRestriction = new ArrayList(); + if (restrictionName == null) + listRestriction.addAll(PrivacyManager.getRestrictions()); + else + listRestriction.add(restrictionName); + + // Create list of restrictions to set + List listPRestriction = new ArrayList(); + for (String rRestrictionName : listRestriction) + listPRestriction.add(new PRestriction(uid, rRestrictionName, methodName, restricted, asked)); + + // Make exceptions + if (methodName == null) + for (String rRestrictionName : listRestriction) + for (Hook md : getHooks(rRestrictionName, null)) { + if (!canRestrict(uid, Process.myUid(), rRestrictionName, md.getName(), false)) + listPRestriction.add(new PRestriction(uid, rRestrictionName, md.getName(), false, true)); + else if (md.isDangerous()) + listPRestriction.add(new PRestriction(uid, rRestrictionName, md.getName(), false, md + .whitelist() == null)); + } + + setRestrictionList(listPRestriction); + } + + public static List cIDCant = Arrays.asList(new String[] { "getString", "Srv_Android_ID", "%serialno", + "SERIAL" }); + + public static boolean canRestrict(int uid, int xuid, String restrictionName, String methodName, boolean system) { + int _uid = Util.getAppId(uid); + int userId = Util.getUserId(uid); + + if (_uid == Process.SYSTEM_UID) { + if (PrivacyManager.cIdentification.equals(restrictionName)) + return false; + if (PrivacyManager.cShell.equals(restrictionName) && "loadLibrary".equals(methodName)) + return false; + } + + if (system) + if (!isApplication(_uid)) + if (!getSettingBool(userId, PrivacyManager.cSettingSystem, false)) + return false; + + // @formatter:off + if (_uid == Util.getAppId(xuid) && + ((PrivacyManager.cIdentification.equals(restrictionName) && cIDCant.contains(methodName)) + || PrivacyManager.cIPC.equals(restrictionName) + || PrivacyManager.cStorage.equals(restrictionName) + || PrivacyManager.cSystem.equals(restrictionName) + || PrivacyManager.cView.equals(restrictionName))) + return false; + // @formatter:on + + Hook hook = getHook(restrictionName, methodName); + if (hook != null && hook.isUnsafe()) + if (getSettingBool(userId, PrivacyManager.cSettingSafeMode, false)) + return false; + + return true; + } + + public static void updateState(int uid) { + setSetting(uid, cSettingState, Integer.toString(ApplicationInfoEx.STATE_CHANGED)); + setSetting(uid, cSettingModifyTime, Long.toString(System.currentTimeMillis())); + } + + public static void setRestrictionList(List listRestriction) { + checkCaller(); + + if (listRestriction.size() > 0) + try { + PrivacyService.getClient().setRestrictionList(listRestriction); + + // Clear cache + synchronized (mRestrictionCache) { + mRestrictionCache.clear(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public static List getRestrictionList(int uid, String restrictionName) { + checkCaller(); + + try { + return PrivacyService.getClient().getRestrictionList(new PRestriction(uid, restrictionName, null, false)); + } catch (Throwable ex) { + Util.bug(null, ex); + } + return new ArrayList(); + } + + public static boolean isRestrictionSet(PRestriction restriction) { + try { + return PrivacyService.getClient().isRestrictionSet(restriction); + } catch (Throwable ex) { + Util.bug(null, ex); + return false; + } + } + + public static void deleteRestrictions(int uid, String restrictionName, boolean deleteWhitelists) { + checkCaller(); + + try { + // Delete restrictions + PrivacyService.getClient().deleteRestrictions(uid, restrictionName == null ? "" : restrictionName); + + // Clear associated whitelists + if (deleteWhitelists && uid > 0) { + for (PSetting setting : getSettingList(uid, null)) + if (Meta.isWhitelist(setting.type)) + setSetting(uid, setting.type, setting.name, null); + } + + // Clear cache + synchronized (mRestrictionCache) { + mRestrictionCache.clear(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + + // Mark as new/changed + setSetting(uid, cSettingState, Integer.toString(restrictionName == null ? ApplicationInfoEx.STATE_CHANGED + : ApplicationInfoEx.STATE_ATTENTION)); + + // Change app modification time + setSetting(uid, cSettingModifyTime, Long.toString(System.currentTimeMillis())); + } + + public static List getRestartStates(int uid, String restrictionName) { + // Returns a list of restriction states for functions whose application + // requires the app to be restarted. + List listRestartRestriction = new ArrayList(); + + Set listRestriction = new HashSet(); + if (restrictionName == null) + listRestriction = mRestart.keySet(); + else if (mRestart.keySet().contains(restrictionName)) + listRestriction.add(restrictionName); + + try { + for (String restriction : listRestriction) { + for (String method : mRestart.get(restriction)) + listRestartRestriction.add(getRestrictionEx(uid, restriction, method).restricted); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + + return listRestartRestriction; + } + + public static void applyTemplate(int uid, String templateName, String restrictionName, boolean methods, + boolean clear, boolean invert) { + checkCaller(); + + int userId = Util.getUserId(uid); + + // Check on-demand + boolean ondemand = getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + + // Build list of restrictions + List listRestriction = new ArrayList(); + if (restrictionName == null) + listRestriction.addAll(getRestrictions()); + else + listRestriction.add(restrictionName); + + // Apply template + Util.log(null, Log.WARN, "Applying template=" + templateName); + boolean hasOndemand = false; + List listPRestriction = new ArrayList(); + for (String rRestrictionName : listRestriction) { + // Cleanup + if (clear) + deleteRestrictions(uid, rRestrictionName, false); + + // Parent + String parentValue = getSetting(userId, templateName, rRestrictionName, Boolean.toString(!ondemand) + + "+ask"); + boolean parentRestricted = parentValue.contains("true"); + boolean parentAsked = (!ondemand || parentValue.contains("asked")); + hasOndemand = hasOndemand || !parentAsked; + + // Merge + PRestriction parentMerge; + if (clear) + parentMerge = new PRestriction(uid, rRestrictionName, null, parentRestricted, parentAsked); + else + parentMerge = getRestrictionEx(uid, rRestrictionName, null); + + // Apply + if (canRestrict(uid, Process.myUid(), rRestrictionName, null, true)) + if (invert && ((parentRestricted && parentMerge.restricted) || (!parentAsked && !parentMerge.asked))) { + listPRestriction.add(new PRestriction(uid, rRestrictionName, null, parentRestricted ? false + : parentMerge.restricted, !parentAsked ? true : parentMerge.asked)); + continue; // leave functions + } else + listPRestriction.add(new PRestriction(uid, rRestrictionName, null, parentMerge.restricted + || parentRestricted, parentMerge.asked && parentAsked)); + + // Childs + if (methods) + for (Hook hook : getHooks(rRestrictionName, null)) + if (canRestrict(uid, Process.myUid(), rRestrictionName, hook.getName(), true)) { + // Child + String settingName = rRestrictionName + "." + hook.getName(); + String childValue = getSetting(userId, templateName, settingName, null); + if (childValue == null) + childValue = Boolean.toString(parentRestricted && !hook.isDangerous()) + + (parentAsked || (hook.isDangerous() && hook.whitelist() == null) ? "+asked" + : "+ask"); + boolean restricted = childValue.contains("true"); + boolean asked = (!ondemand || childValue.contains("asked")); + + // Merge + PRestriction childMerge; + if (clear) + childMerge = new PRestriction(uid, rRestrictionName, hook.getName(), parentRestricted + && restricted, parentAsked || asked); + else + childMerge = getRestrictionEx(uid, rRestrictionName, hook.getName()); + + // Invert + if (invert && parentRestricted && restricted) { + restricted = false; + childMerge.restricted = false; + } + if (invert && !parentAsked && !asked) { + asked = true; + childMerge.asked = true; + } + + // Apply + if ((parentRestricted && !restricted) || (!parentAsked && asked) + || (invert ? false : hook.isDangerous() || !clear)) { + PRestriction child = new PRestriction(uid, rRestrictionName, hook.getName(), + (parentRestricted && restricted) || childMerge.restricted, (parentAsked || asked) + && childMerge.asked); + listPRestriction.add(child); + } + } + } + + // Apply result + setRestrictionList(listPRestriction); + if (hasOndemand) + PrivacyManager.setSetting(uid, PrivacyManager.cSettingOnDemand, Boolean.toString(true)); + } + + // White/black listing + + public static Map> listWhitelisted(int uid, String type) { + checkCaller(); + + Map> mapWhitelisted = new HashMap>(); + for (PSetting setting : getSettingList(uid, type)) + if (Meta.isWhitelist(setting.type)) { + if (!mapWhitelisted.containsKey(setting.type)) + mapWhitelisted.put(setting.type, new TreeMap()); + mapWhitelisted.get(setting.type).put(setting.name, Boolean.parseBoolean(setting.value)); + } + return mapWhitelisted; + } + + // Usage + + public static long getUsage(int uid, String restrictionName, String methodName) { + checkCaller(); + + try { + List listRestriction = new ArrayList(); + if (restrictionName == null) + for (String sRestrictionName : getRestrictions()) + listRestriction.add(new PRestriction(uid, sRestrictionName, methodName, false)); + else + listRestriction.add(new PRestriction(uid, restrictionName, methodName, false)); + return PrivacyService.getClient().getUsage(listRestriction); + } catch (Throwable ex) { + Util.bug(null, ex); + return 0; + } + } + + public static List getUsageList(Context context, int uid, String restrictionName) { + checkCaller(); + + List listUsage = new ArrayList(); + try { + listUsage.addAll(PrivacyService.getClient().getUsageList(uid, + restrictionName == null ? "" : restrictionName)); + } catch (Throwable ex) { + Util.log(null, Log.ERROR, "getUsageList"); + Util.bug(null, ex); + } + Collections.sort(listUsage, new ParcelableRestrictionCompare()); + return listUsage; + } + + public static class ParcelableRestrictionCompare implements Comparator { + @Override + public int compare(PRestriction one, PRestriction another) { + if (one.time < another.time) + return 1; + else if (one.time > another.time) + return -1; + else + return 0; + } + } + + public static void deleteUsage(int uid) { + checkCaller(); + + try { + PrivacyService.getClient().deleteUsage(uid); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + // Settings + + public static String getSalt(int userId) { + String def = (Build.SERIAL == null ? "" : Build.SERIAL); + return getSetting(userId, cSettingSalt, def); + } + + public static void removeLegacySalt(int userId) { + String def = (Build.SERIAL == null ? "" : Build.SERIAL); + String salt = getSetting(userId, cSettingSalt, null); + if (def.equals(salt)) + setSetting(userId, cSettingSalt, null); + } + + public static boolean getSettingBool(int uid, String name, boolean defaultValue) { + return Boolean.parseBoolean(getSetting(uid, name, Boolean.toString(defaultValue))); + } + + public static boolean getSettingBool(int uid, String type, String name, boolean defaultValue) { + return Boolean.parseBoolean(getSetting(uid, type, name, Boolean.toString(defaultValue))); + } + + public static String getSetting(int uid, String name, String defaultValue) { + return getSetting(uid, "", name, defaultValue); + } + + public static String getSetting(int uid, String type, String name, String defaultValue) { + long start = System.currentTimeMillis(); + String value = null; + + // Check cache + boolean cached = false; + boolean willExpire = false; + CSetting key = new CSetting(uid, type, name); + synchronized (mSettingsCache) { + if (mSettingsCache.containsKey(key)) { + CSetting entry = mSettingsCache.get(key); + if (!entry.isExpired()) { + cached = true; + value = entry.getValue(); + willExpire = entry.willExpire(); + } + } + } + + // Get settings + if (!cached) + try { + value = PrivacyService.getSettingProxy(new PSetting(Math.abs(uid), type, name, null)).value; + if (value == null) + if (uid > 99) { + int userId = Util.getUserId(uid); + value = PrivacyService.getSettingProxy(new PSetting(userId, type, name, null)).value; + } + + // Add to cache + if (value == null) + key.setValue(defaultValue); + else + key.setValue(value); + synchronized (mSettingsCache) { + if (mSettingsCache.containsKey(key)) + mSettingsCache.remove(key); + mSettingsCache.put(key, key); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + + if (value == null) + value = defaultValue; + + long ms = System.currentTimeMillis() - start; + if (!willExpire && !cSettingLog.equals(name)) + Util.log(null, ms < cWarnServiceDelayMs ? Log.INFO : Log.WARN, String.format( + "Get setting uid=%d %s/%s=%s%s %d ms", uid, type, name, value, (cached ? " (cached)" : ""), ms)); + + return value; + } + + public static void setSetting(int uid, String name, String value) { + setSetting(uid, "", name, value); + } + + public static void setSetting(int uid, String type, String name, String value) { + checkCaller(); + + try { + PrivacyService.getClient().setSetting(new PSetting(uid, type, name, value)); + + // Update cache + CSetting key = new CSetting(uid, type, name); + key.setValue(value); + synchronized (mSettingsCache) { + if (mSettingsCache.containsKey(key)) + mSettingsCache.remove(key); + mSettingsCache.put(key, key); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public static void setSettingList(List listSetting) { + checkCaller(); + + if (listSetting.size() > 0) + try { + PrivacyService.getClient().setSettingList(listSetting); + + // Clear cache + synchronized (mSettingsCache) { + mSettingsCache.clear(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public static List getSettingList(int uid, String type) { + checkCaller(); + + try { + return PrivacyService.getClient().getSettingList(new PSetting(uid, type, null, null)); + } catch (Throwable ex) { + Util.bug(null, ex); + } + return new ArrayList(); + } + + public static void deleteSettings(int uid) { + checkCaller(); + + try { + PrivacyService.getClient().deleteSettings(uid); + + // Clear cache + synchronized (mSettingsCache) { + mSettingsCache.clear(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + private static final List cSettingAppSpecific = Arrays.asList(new String[] { cSettingRandom, + cSettingSerial, cSettingLatitude, cSettingLongitude, cSettingAltitude, cSettingMac, cSettingIP, + cSettingImei, cSettingPhone, cSettingId, cSettingGsfId, cSettingAdId, cSettingMcc, cSettingMnc, + cSettingCountry, cSettingOperator, cSettingIccId, cSettingCid, cSettingLac, cSettingSubscriber, + cSettingSSID, cSettingUa }); + + public static boolean hasSpecificSettings(int uid) { + boolean specific = false; + for (PSetting setting : getSettingList(uid, "")) + if (cSettingAppSpecific.contains(setting.name)) { + specific = true; + break; + } + return specific; + } + + public static String getTransient(String name, String defaultValue) { + CSetting csetting = new CSetting(0, "", name); + synchronized (mTransientCache) { + if (mTransientCache.containsKey(csetting)) + return mTransientCache.get(csetting).getValue(); + } + + return defaultValue; + } + + public static void setTransient(String name, String value) { + CSetting setting = new CSetting(0, "", name); + setting.setValue(value); + synchronized (mTransientCache) { + mTransientCache.put(setting, setting); + } + } + + // Common + + public static void clear() { + checkCaller(); + + try { + PrivacyService.getClient().clear(); + flush(); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public static void flush() { + synchronized (mSettingsCache) { + mSettingsCache.clear(); + } + synchronized (mRestrictionCache) { + mRestrictionCache.clear(); + } + synchronized (mPermissionRestrictionCache) { + mPermissionRestrictionCache.clear(); + } + synchronized (mPermissionHookCache) { + mPermissionHookCache.clear(); + } + } + + // Defacing + + @SuppressLint("DefaultLocale") + public static Object getDefacedProp(int uid, String name) { + // Serial number + if (name.equals("SERIAL") || name.equals("%serialno")) { + String value = getSetting(uid, cSettingSerial, cDeface); + return (cValueRandom.equals(value) ? getRandomProp("SERIAL") : value); + } + + // Host name + if (name.equals("%hostname")) + return cDeface; + + // MAC addresses + if (name.equals("MAC") || name.equals("%macaddr")) { + String mac = getSetting(uid, cSettingMac, "DE:FA:CE:DE:FA:CE"); + if (cValueRandom.equals(mac)) + return getRandomProp("MAC"); + StringBuilder sb = new StringBuilder(mac.replace(":", "")); + while (sb.length() != 12) + sb.insert(0, '0'); + while (sb.length() > 12) + sb.deleteCharAt(sb.length() - 1); + for (int i = 10; i > 0; i -= 2) + sb.insert(i, ':'); + return sb.toString(); + } + + // cid + if (name.equals("%cid")) + return cDeface; + + // IMEI + if (name.equals("getDeviceId") || name.equals("%imei")) { + String value = getSetting(uid, cSettingImei, "000000000000000"); + return (cValueRandom.equals(value) ? getRandomProp("IMEI") : value); + } + + // Phone + if (name.equals("PhoneNumber") || name.equals("getLine1AlphaTag") || name.equals("getLine1Number") + || name.equals("getMsisdn") || name.equals("getVoiceMailAlphaTag") || name.equals("getVoiceMailNumber") + || name.equals("getCompleteVoiceMailNumber")) { + String value = getSetting(uid, cSettingPhone, cDeface); + return (cValueRandom.equals(value) ? getRandomProp("PHONE") : value); + } + + // Android ID + if (name.equals("ANDROID_ID")) { + String value = getSetting(uid, cSettingId, cDeface); + return (cValueRandom.equals(value) ? getRandomProp("ANDROID_ID") : value); + } + + // Telephony manager + if (name.equals("getGroupIdLevel1")) + return null; + if (name.equals("getIsimDomain")) + return null; + if (name.equals("getIsimImpi")) + return null; + if (name.equals("getIsimImpu")) + return null; + + if (name.equals("getNetworkCountryIso") || name.equals("CountryIso")) { + // ISO country code + String value = getSetting(uid, cSettingCountry, "XX"); + return (cValueRandom.equals(value) ? getRandomProp("ISO3166") : value); + } + if (name.equals("getNetworkOperator")) + // MCC+MNC: test network + return getSetting(uid, cSettingMcc, "001") + getSetting(uid, cSettingMnc, "01"); + if (name.equals("getNetworkOperatorName")) + return getSetting(uid, cSettingOperator, cDeface); + + if (name.equals("getSimCountryIso")) { + // ISO country code + String value = getSetting(uid, cSettingCountry, "XX"); + return (cValueRandom.equals(value) ? getRandomProp("ISO3166") : value); + } + if (name.equals("getSimOperator")) + // MCC+MNC: test network + return getSetting(uid, cSettingMcc, "001") + getSetting(uid, cSettingMnc, "01"); + if (name.equals("getSimOperatorName")) + return getSetting(uid, cSettingOperator, cDeface); + + if (name.equals("getSimSerialNumber") || name.equals("getIccSerialNumber") || name.equals("getIccSerialNumber")) + return getSetting(uid, cSettingIccId, null); + + if (name.equals("getSubscriberId")) { // IMSI for a GSM phone + String value = getSetting(uid, cSettingSubscriber, null); + return (cValueRandom.equals(value) ? getRandomProp("SubscriberId") : value); + } + + if (name.equals("SSID")) { + // Default hidden network + String value = getSetting(uid, cSettingSSID, ""); + return (cValueRandom.equals(value) ? getRandomProp("SSID") : value); + } + + // Google services framework ID + if (name.equals("GSF_ID")) { + long gsfid = 0xDEFACE; + try { + String value = getSetting(uid, cSettingGsfId, "DEFACE"); + if (cValueRandom.equals(value)) + value = getRandomProp(name); + gsfid = Long.parseLong(value.toLowerCase(), 16); + } catch (Throwable ignored) { + } + return gsfid; + } + + // Advertisement ID + if (name.equals("AdvertisingId")) { + String adid = getSetting(uid, cSettingAdId, "DEFACE00-0000-0000-0000-000000000000"); + if (cValueRandom.equals(adid)) + adid = getRandomProp(name); + return adid; + } + + if (name.equals("InetAddress")) { + // Set address + String ip = getSetting(uid, cSettingIP, null); + if (ip != null) + try { + return InetAddress.getByName(ip); + } catch (Throwable ignored) { + } + + // Any address (0.0.0.0) + try { + Field unspecified = Inet4Address.class.getDeclaredField("ANY"); + unspecified.setAccessible(true); + return (InetAddress) unspecified.get(Inet4Address.class); + } catch (Throwable ex) { + Util.bug(null, ex); + return null; + } + } + + if (name.equals("IPInt")) { + // Set address + String ip = getSetting(uid, cSettingIP, null); + if (ip != null) + try { + InetAddress inet = InetAddress.getByName(ip); + if (inet.getClass().equals(Inet4Address.class)) { + byte[] b = inet.getAddress(); + return b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + + // Any address (0.0.0.0) + return 0; + } + + if (name.equals("Bytes3")) + return new byte[] { (byte) 0xDE, (byte) 0xFA, (byte) 0xCE }; + + if (name.equals("UA")) + return getSetting(uid, cSettingUa, + "Mozilla/5.0 (Linux; U; Android; en-us) AppleWebKit/999+ (KHTML, like Gecko) Safari/999.9"); + + // InputDevice + if (name.equals("DeviceDescriptor")) + return cDeface; + + // getExtraInfo + if (name.equals("ExtraInfo")) + return cDeface; + + if (name.equals("MCC")) + return getSetting(uid, cSettingMcc, "001"); + + if (name.equals("MNC")) + return getSetting(uid, cSettingMnc, "01"); + + if (name.equals("CID")) + try { + return Integer.parseInt(getSetting(uid, cSettingCid, "0")) & 0xFFFF; + } catch (Throwable ignored) { + return -1; + } + + if (name.equals("LAC")) + try { + return Integer.parseInt(getSetting(uid, cSettingLac, "0")) & 0xFFFF; + } catch (Throwable ignored) { + return -1; + } + + if (name.equals("USB")) + return cDeface; + + if (name.equals("BTName")) + return cDeface; + + if (name.equals("CastID")) + return cDeface; + + // Fallback + Util.log(null, Log.ERROR, "Fallback value name=" + name); + Util.logStack(null, Log.ERROR); + return cDeface; + } + + public static Location getDefacedLocation(int uid, Location location) { + // Christmas Island ~ -10.5 / 105.667 + String sLat = getSetting(uid, cSettingLatitude, "-10.5"); + String sLon = getSetting(uid, cSettingLongitude, "105.667"); + String sAlt = getSetting(uid, cSettingAltitude, "686"); + + // Backward compatibility + if ("".equals(sLat)) + sLat = "-10.5"; + if ("".equals(sLon)) + sLon = "105.667"; + + if (cValueRandom.equals(sLat)) + sLat = getRandomProp("LAT"); + if (cValueRandom.equals(sLon)) + sLon = getRandomProp("LON"); + if (cValueRandom.equals(sAlt)) + sAlt = getRandomProp("ALT"); + + // 1 degree ~ 111111 m + // 1 m ~ 0,000009 degrees + if (location == null) + location = new Location(cDeface); + location.setLatitude(Float.parseFloat(sLat) + (Math.random() * 2.0 - 1.0) * location.getAccuracy() * 9e-6); + location.setLongitude(Float.parseFloat(sLon) + (Math.random() * 2.0 - 1.0) * location.getAccuracy() * 9e-6); + location.setAltitude(Float.parseFloat(sAlt) + (Math.random() * 2.0 - 1.0) * location.getAccuracy()); + + return location; + } + + @SuppressLint("DefaultLocale") + public static String getRandomProp(String name) { + Random r = new Random(); + + if (name.equals("SERIAL")) { + long v = r.nextLong(); + return Long.toHexString(v).toUpperCase(); + } + + if (name.equals("MAC")) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 6; i++) { + if (i != 0) + sb.append(':'); + int v = r.nextInt(256); + if (i == 0) + v = v & 0xFC; // unicast, globally unique + sb.append(Integer.toHexString(0x100 | v).substring(1)); + } + return sb.toString().toUpperCase(); + } + + // IMEI/MEID + if (name.equals("IMEI")) { + // http://en.wikipedia.org/wiki/Reporting_Body_Identifier + String[] rbi = new String[] { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "30", "33", + "35", "44", "45", "49", "50", "51", "52", "53", "54", "86", "91", "98", "99" }; + String imei = rbi[r.nextInt(rbi.length)]; + while (imei.length() < 14) + imei += Character.forDigit(r.nextInt(10), 10); + imei += getLuhnDigit(imei); + return imei; + } + + if (name.equals("PHONE")) { + String phone = "0"; + for (int i = 1; i < 10; i++) + phone += Character.forDigit(r.nextInt(10), 10); + return phone; + } + + if (name.equals("ANDROID_ID")) { + long v = r.nextLong(); + return Long.toHexString(v); + } + + if (name.equals("ISO3166")) { + String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + String country = Character.toString(letters.charAt(r.nextInt(letters.length()))) + + Character.toString(letters.charAt(r.nextInt(letters.length()))); + return country; + } + + if (name.equals("GSF_ID")) { + long v = Math.abs(r.nextLong()); + return Long.toString(v, 16).toUpperCase(); + } + + if (name.equals("AdvertisingId")) + return UUID.randomUUID().toString().toUpperCase(); + + if (name.equals("LAT")) { + double d = r.nextDouble() * 180 - 90; + d = Math.rint(d * 1e7) / 1e7; + return Double.toString(d); + } + + if (name.equals("LON")) { + double d = r.nextDouble() * 360 - 180; + d = Math.rint(d * 1e7) / 1e7; + return Double.toString(d); + } + + if (name.equals("ALT")) { + double d = r.nextDouble() * 2 * 686; + return Double.toString(d); + } + + if (name.equals("SubscriberId")) { + String subscriber = "00101"; + while (subscriber.length() < 15) + subscriber += Character.forDigit(r.nextInt(10), 10); + return subscriber; + } + + if (name.equals("SSID")) { + String ssid = ""; + while (ssid.length() < 6) + ssid += (char) (r.nextInt(26) + 'A'); + + ssid += Character.forDigit(r.nextInt(10), 10); + ssid += Character.forDigit(r.nextInt(10), 10); + return ssid; + } + + return ""; + } + + private static char getLuhnDigit(String x) { + // http://en.wikipedia.org/wiki/Luhn_algorithm + int sum = 0; + for (int i = 0; i < x.length(); i++) { + int n = Character.digit(x.charAt(x.length() - 1 - i), 10); + if (i % 2 == 0) { + n *= 2; + if (n > 9) + n -= 9; // n = (n % 10) + 1; + } + sum += n; + } + return Character.forDigit((sum * 9) % 10, 10); + } + + // Helper methods + + public static void checkCaller() { + if (PrivacyService.isRegistered()) { + Util.log(null, Log.ERROR, "Privacy manager call from service"); + Util.logStack(null, Log.ERROR); + } + } + + public static final int FIRST_ISOLATED_UID = 99000; + public static final int LAST_ISOLATED_UID = 99999; + public static final int FIRST_SHARED_APPLICATION_GID = 50000; + public static final int LAST_SHARED_APPLICATION_GID = 59999; + + public static boolean isApplication(int uid) { + uid = Util.getAppId(uid); + return (uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID); + } + + public static boolean isShared(int uid) { + uid = Util.getAppId(uid); + return (uid >= FIRST_SHARED_APPLICATION_GID && uid <= LAST_SHARED_APPLICATION_GID); + } + + public static boolean isIsolated(int uid) { + uid = Util.getAppId(uid); + return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID); + } + + public static boolean hasPermission(Context context, ApplicationInfoEx appInfo, String restrictionName, + Version version) { + int uid = appInfo.getUid(); + synchronized (mPermissionRestrictionCache) { + if (mPermissionRestrictionCache.get(uid) == null) + mPermissionRestrictionCache.append(uid, new HashMap()); + if (!mPermissionRestrictionCache.get(uid).containsKey(restrictionName)) { + boolean permission = hasPermission(context, appInfo.getPackageName(), + getPermissions(restrictionName, version)); + mPermissionRestrictionCache.get(uid).put(restrictionName, permission); + } + return mPermissionRestrictionCache.get(uid).get(restrictionName); + } + } + + public static boolean hasPermission(Context context, ApplicationInfoEx appInfo, Hook md) { + int uid = appInfo.getUid(); + synchronized (mPermissionHookCache) { + if (mPermissionHookCache.get(uid) == null) + mPermissionHookCache.append(uid, new HashMap()); + if (!mPermissionHookCache.get(uid).containsKey(md)) { + + List listPermission = (md.getPermissions() == null ? null : Arrays.asList(md.getPermissions())); + boolean permission = hasPermission(context, appInfo.getPackageName(), listPermission); + mPermissionHookCache.get(uid).put(md, permission); + } + return mPermissionHookCache.get(uid).get(md); + } + } + + public static void clearPermissionCache(int uid) { + synchronized (mPermissionRestrictionCache) { + if (mPermissionRestrictionCache.get(uid) != null) + mPermissionRestrictionCache.remove(uid); + } + synchronized (mPermissionHookCache) { + if (mPermissionHookCache.get(uid) != null) + mPermissionHookCache.remove(uid); + } + } + + @SuppressLint("DefaultLocale") + private static boolean hasPermission(Context context, List listPackage, List listPermission) { + try { + if (listPermission == null || listPermission.size() == 0 || listPermission.contains("")) + return true; + + PackageManager pm = context.getPackageManager(); + for (String packageName : listPackage) { + // Check absolute permissions + for (String apermission : listPermission) + if (apermission.contains(".")) + if (pm.checkPermission(apermission, packageName) == PackageManager.PERMISSION_GRANTED) + return true; + + // Check relative permissions + PackageInfo pInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + if (pInfo != null && pInfo.requestedPermissions != null) + for (String rPermission : pInfo.requestedPermissions) + for (String permission : listPermission) + if (rPermission.toLowerCase().contains(permission.toLowerCase())) { + String aPermission = "android.permission." + permission; + if (!aPermission.equals(rPermission)) + Util.log(null, Log.WARN, "Check permission=" + permission + "/" + rPermission); + return true; + } + } + } catch (NameNotFoundException ex) { + Util.log(null, Log.WARN, ex.toString()); + } catch (Throwable ex) { + Util.bug(null, ex); + } + return false; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyProvider.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyProvider.java new file mode 100644 index 0000000..d50a8ba --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyProvider.java @@ -0,0 +1,759 @@ +package biz.bokhorst.xprivacy; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import android.annotation.SuppressLint; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.UriMatcher; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.os.Binder; +import android.os.Process; +import android.util.Log; + +// This code is legacy and only used for updating to newer versions + +@SuppressWarnings("deprecation") +@SuppressLint({ "DefaultLocale", "WorldReadableFiles", "Registered" }) +public class PrivacyProvider extends ContentProvider { + private static final String AUTHORITY = "biz.bokhorst.xprivacy.provider"; + private static final String PREF_RESTRICTION = AUTHORITY; + private static final String PREF_USAGE = AUTHORITY + ".usage"; + private static final String PREF_SETTINGS = AUTHORITY + ".settings"; + private static final String PATH_RESTRICTION = "restriction"; + private static final String PATH_USAGE = "usage"; + private static final String PATH_SETTINGS = "settings"; + + public static final Uri URI_RESTRICTION = Uri.parse("content://" + AUTHORITY + "/" + PATH_RESTRICTION); + public static final Uri URI_USAGE = Uri.parse("content://" + AUTHORITY + "/" + PATH_USAGE); + public static final Uri URI_SETTING = Uri.parse("content://" + AUTHORITY + "/" + PATH_SETTINGS); + + public static final String COL_UID = "Uid"; + public static final String COL_RESTRICTION = "Restriction"; + public static final String COL_RESTRICTED = "Restricted"; + public static final String COL_METHOD = "Method"; + public static final String COL_USED = "Used"; + public static final String COL_SETTING = "Setting"; + public static final String COL_VALUE = "Value"; + + private static final UriMatcher sUriMatcher; + private static final int TYPE_RESTRICTION = 1; + private static final int TYPE_USAGE = 2; + private static final int TYPE_SETTING = 3; + + private static Object mFallbackRestrictionLock = new Object(); + private static Object mFallbackSettingsLock = new Object(); + private static int mFallbackRestrictionsUid = 0; + private static long mFallbackRestrictionsTime = 0; + private static long mFallbackSettingsTime = 0; + private static SharedPreferencesEx mFallbackRestrictions = null; + private static SharedPreferencesEx mFallbackSettings = null; + + private static ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + static { + sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + sUriMatcher.addURI(AUTHORITY, PATH_RESTRICTION, TYPE_RESTRICTION); + sUriMatcher.addURI(AUTHORITY, PATH_USAGE, TYPE_USAGE); + sUriMatcher.addURI(AUTHORITY, PATH_SETTINGS, TYPE_SETTING); + } + + @Override + public boolean onCreate() { + try { + convertRestrictions(getContext()); + convertSettings(getContext()); + fixFilePermissions(); + } catch (Throwable ex) { + Util.bug(null, ex); + } + return true; + } + + @Override + public String getType(Uri uri) { + if (sUriMatcher.match(uri) == TYPE_RESTRICTION) + return String.format("vnd.android.cursor.dir/%s.%s", AUTHORITY, PATH_RESTRICTION); + else if (sUriMatcher.match(uri) == TYPE_USAGE) + return String.format("vnd.android.cursor.dir/%s.%s", AUTHORITY, PATH_USAGE); + else if (sUriMatcher.match(uri) == TYPE_SETTING) + return String.format("vnd.android.cursor.dir/%s.%s", AUTHORITY, PATH_SETTINGS); + throw new IllegalArgumentException(); + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + if (sUriMatcher.match(uri) == TYPE_RESTRICTION && selectionArgs != null && selectionArgs.length >= 2) { + // Query restrictions + String restrictionName = selection; + int uid = Integer.parseInt(selectionArgs[0]); + boolean usage = Boolean.parseBoolean(selectionArgs[1]); + String methodName = (selectionArgs.length >= 3 ? selectionArgs[2] : null); + + return queryRestrictions(uid, restrictionName, methodName, usage); + } else if (sUriMatcher.match(uri) == TYPE_USAGE && selectionArgs != null && selectionArgs.length >= 1) { + // Query usage + String restrictionName = selection; + int uid = Integer.parseInt(selectionArgs[0]); + String methodName = (selectionArgs.length >= 2 ? selectionArgs[1] : null); + + return queryUsage(uid, restrictionName, methodName); + } else if (sUriMatcher.match(uri) == TYPE_SETTING && selectionArgs == null) + return querySettings(selection); + + throw new IllegalArgumentException(uri.toString()); + } + + private Cursor queryRestrictions(final int uid, final String restrictionName, final String methodName, boolean usage) { + MatrixCursor cursor = new MatrixCursor(new String[] { COL_UID, COL_RESTRICTION, COL_METHOD, COL_RESTRICTED }); + + // Build restriction list + List listRestrictionName; + if (restrictionName == null) + listRestrictionName = PrivacyManager.getRestrictions(); + else { + listRestrictionName = new ArrayList(); + listRestrictionName.add(restrictionName); + } + + if (uid == 0) { + // Process applications + PackageManager pm = getContext().getPackageManager(); + for (ApplicationInfo appInfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION + "." + appInfo.uid, + Context.MODE_WORLD_READABLE); + + // Process restrictions + for (String eRestrictionName : listRestrictionName) + if (getRestricted(eRestrictionName, null, prefs)) { + // Category + cursor.addRow(new Object[] { appInfo.uid, eRestrictionName, null, true }); + + // Exceptions + for (Hook md : PrivacyManager.getHooks(eRestrictionName, null)) { + boolean restricted = getRestricted(eRestrictionName, md.getName(), prefs); + if (!restricted || md.isDangerous()) + cursor.addRow(new Object[] { appInfo.uid, eRestrictionName, md.getName(), restricted }); + } + } + } + } else { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION + "." + uid, + Context.MODE_WORLD_READABLE); + + // Process restrictions + boolean restricted = false; + if (methodName != null && methodName.equals("*")) { + for (String eRestrictionName : listRestrictionName) { + boolean eRestricted = getRestricted(eRestrictionName, null, prefs); + cursor.addRow(new Object[] { uid, eRestrictionName, null, Boolean.toString(eRestricted) }); + for (Hook md : PrivacyManager.getHooks(eRestrictionName, null)) { + eRestricted = getRestricted(eRestrictionName, md.getName(), prefs); + cursor.addRow(new Object[] { uid, eRestrictionName, md.getName(), Boolean.toString(eRestricted) }); + } + } + } else { + for (String eRestrictionName : listRestrictionName) { + boolean eRestricted = getRestricted(eRestrictionName, methodName, prefs); + cursor.addRow(new Object[] { uid, eRestrictionName, methodName, Boolean.toString(eRestricted) }); + restricted = (restricted || eRestricted); + } + } + + // Update usage data + if (usage && restrictionName != null && methodName != null && !methodName.equals("*")) { + final boolean isRestricted = restricted; + final long timeStamp = new Date().getTime(); + mExecutor.execute(new Runnable() { + public void run() { + updateUsage(uid, restrictionName, methodName, isRestricted, timeStamp); + } + }); + } + } + + return cursor; + } + + private static boolean getRestricted(String restrictionName, String methodName, SharedPreferences prefs) { + // Check for restriction + boolean restricted = prefs.getBoolean(getRestrictionPref(restrictionName), false); + + // Check for exception + if (restricted && methodName != null) + if (prefs.getBoolean(getExceptionPref(restrictionName, methodName), false)) + restricted = false; + + return restricted; + } + + private Cursor queryUsage(int uid, String restrictionName, String methodName) { + MatrixCursor cursor = new MatrixCursor(new String[] { COL_UID, COL_RESTRICTION, COL_METHOD, COL_RESTRICTED, + COL_USED }); + + List listRestriction; + if (restrictionName == null) + listRestriction = PrivacyManager.getRestrictions(); + else { + listRestriction = new ArrayList(); + listRestriction.add(restrictionName); + } + + if (uid == 0) { + // All + for (String eRestrictionName : PrivacyManager.getRestrictions()) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + eRestrictionName, + Context.MODE_PRIVATE); + for (String prefName : prefs.getAll().keySet()) + if (prefName.startsWith(COL_USED)) { + String[] prefParts = prefName.split("\\."); + int rUid = Integer.parseInt(prefParts[1]); + String rMethodName = prefName.substring(prefParts[0].length() + 1 + prefParts[1].length() + 1); + getUsage(rUid, eRestrictionName, rMethodName, cursor); + } + } + } else { + // Selected restrictions/methods + for (String eRestrictionName : listRestriction) + if (methodName == null) + for (Hook md : PrivacyManager.getHooks(eRestrictionName, null)) + getUsage(uid, eRestrictionName, md.getName(), cursor); + else + getUsage(uid, eRestrictionName, methodName, cursor); + } + return cursor; + } + + private void getUsage(int uid, String restrictionName, String methodName, MatrixCursor cursor) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + restrictionName, + Context.MODE_PRIVATE); + String values = prefs.getString(getUsagePref(uid, methodName), null); + if (values != null) { + String[] value = values.split(":"); + long timeStamp = Long.parseLong(value[0]); + boolean restricted = Boolean.parseBoolean(value[1]); + cursor.addRow(new Object[] { uid, restrictionName, methodName, restricted, timeStamp }); + } + } + + private Cursor querySettings(String name) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); + MatrixCursor cursor = new MatrixCursor(new String[] { COL_SETTING, COL_VALUE }); + if (name == null) + for (String settingKey : prefs.getAll().keySet()) + try { + cursor.addRow(new Object[] { getSettingName(settingKey), prefs.getString(settingKey, null) }); + } catch (Throwable ex) { + // Legacy boolean + } + else + cursor.addRow(new Object[] { name, prefs.getString(getSettingPref(name), null) }); + return cursor; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + // Check access + enforcePermission(); + + throw new IllegalArgumentException(uri.toString()); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + if (sUriMatcher.match(uri) == TYPE_RESTRICTION) { + // Check access + enforcePermission(); + + // Get arguments + String restrictionName = selection; + int uid = values.getAsInteger(COL_UID); + String methodName = values.getAsString(COL_METHOD); + boolean restricted = Boolean.parseBoolean(values.getAsString(COL_RESTRICTED)); + updateRestriction(getContext(), uid, restrictionName, methodName, !restricted); + + return 1; // rows + } else if (sUriMatcher.match(uri) == TYPE_USAGE) { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + + // Get arguments + int uid = values.getAsInteger(COL_UID); + String restrictionName = values.getAsString(PrivacyProvider.COL_RESTRICTION); + String methodName = values.getAsString(COL_METHOD); + boolean restricted = false; + if (values.containsKey(PrivacyProvider.COL_RESTRICTED)) + restricted = values.getAsBoolean(PrivacyProvider.COL_RESTRICTED); + long timeStamp = values.getAsLong(PrivacyProvider.COL_USED); + Util.log(null, Log.INFO, + String.format("Update usage data %d/%s/%s=%b", uid, restrictionName, methodName, restricted)); + + // Update usage data + if (methodName != null) + updateUsage(uid, restrictionName, methodName, restricted, timeStamp); + + return 1; + } else if (sUriMatcher.match(uri) == TYPE_SETTING) { + // Check access + enforcePermission(); + + // Get arguments + String settingName = selection; + String value = values.getAsString(COL_VALUE); + + // Update setting + updateSetting(settingName, value); + + return 1; + } + + throw new IllegalArgumentException(uri.toString()); + } + + private static void updateRestriction(Context context, int uid, String restrictionName, String methodName, + boolean allowed) { + // Update restriction + SharedPreferences prefs = context.getSharedPreferences(PREF_RESTRICTION + "." + uid, + Context.MODE_WORLD_READABLE); + SharedPreferences.Editor editor = prefs.edit(); + if (methodName == null || !allowed) + editor.putBoolean(getRestrictionPref(restrictionName), !allowed); + if (methodName != null) + editor.putBoolean(getExceptionPref(restrictionName, methodName), allowed); + editor.commit(); + setPrefFileReadable(PREF_RESTRICTION, uid); + } + + private void updateUsage(final int uid, final String restrictionName, final String methodName, + final boolean restricted, long timeStamp) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + restrictionName, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + String prefName = getUsagePref(uid, methodName); + String prefValue = String.format("%d:%b", timeStamp, restricted); + editor.remove(prefName); + editor.putString(prefName, prefValue); + editor.commit(); + } + + private void updateSetting(String name, String value) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); + SharedPreferences.Editor editor = prefs.edit(); + if (value == null) + editor.remove(getSettingPref(name)); + else + editor.putString(getSettingPref(name), value); + editor.commit(); + setPrefFileReadable(PREF_SETTINGS); + } + + @Override + public int delete(Uri uri, String where, String[] selectionArgs) { + // Check access + enforcePermission(); + + if (sUriMatcher.match(uri) == TYPE_RESTRICTION) { + int uid = Integer.parseInt(selectionArgs[0]); + return deleteRestrictions(uid); + } else if (sUriMatcher.match(uri) == TYPE_USAGE) { + int uid = Integer.parseInt(selectionArgs[0]); + return deleteUsage(uid); + } else if (sUriMatcher.match(uri) == TYPE_SETTING) { + int uid = Integer.parseInt(selectionArgs[0]); + return deleteSettings(uid); + } + + throw new IllegalArgumentException(uri.toString()); + } + + private int deleteRestrictions(int uid) { + int rows = 0; + SharedPreferences prefs = getContext().getSharedPreferences(PREF_RESTRICTION + "." + uid, + Context.MODE_WORLD_READABLE); + SharedPreferences.Editor editor = prefs.edit(); + for (String pref : prefs.getAll().keySet()) { + Util.log(null, Log.INFO, "Removed restriction=" + pref + " uid=" + uid); + editor.remove(pref); + rows++; + } + editor.commit(); + setPrefFileReadable(PREF_RESTRICTION, uid); + return rows; + } + + private int deleteUsage(int uid) { + int rows = 0; + String sUid = Integer.toString(uid); + for (String restrictionName : PrivacyManager.getRestrictions()) { + SharedPreferences prefs = getContext().getSharedPreferences(PREF_USAGE + "." + restrictionName, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + for (String pref : prefs.getAll().keySet()) { + String[] component = pref.split("\\."); + if (uid == 0 || (component.length >= 2 && component[1].equals(sUid))) { + Util.log(null, Log.INFO, "Removed usage=" + pref); + editor.remove(pref); + rows++; + } + } + editor.commit(); + } + return rows; + } + + private int deleteSettings(int uid) { + int rows = 0; + String sUid = Integer.toString(uid); + SharedPreferences prefs = getContext().getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); + SharedPreferences.Editor editor = prefs.edit(); + for (String pref : prefs.getAll().keySet()) { + String[] component = pref.split("\\."); + if (component.length >= 3 && component[2].equals(sUid)) { + Util.log(null, Log.INFO, "Removed setting=" + pref + " uid=" + uid); + editor.remove(pref); + rows++; + } + } + editor.commit(); + setPrefFileReadable(PREF_SETTINGS); + return rows; + } + + // The following methods are used as fallback, when: + // - there is no context (Java threads) + // - the content provider cannot be queried (PackageManagerService) + + public static boolean getRestrictedFallback(XHook hook, int uid, String restrictionName, String methodName) { + try { + long now = new Date().getTime(); + File file = new File(getPrefFileName(PREF_RESTRICTION, uid)); + if (!file.exists()) + Util.log(null, Log.INFO, "Not found file=" + file.getAbsolutePath()); + + synchronized (mFallbackRestrictionLock) { + if (mFallbackRestrictions == null || mFallbackRestrictionsUid != uid) { + // Initial load + mFallbackRestrictions = new SharedPreferencesEx(file); + mFallbackRestrictionsUid = uid; + mFallbackRestrictionsTime = now; + long ms = System.currentTimeMillis() - now; + Util.log(null, Log.INFO, "Load fallback restrictions uid=" + uid + "/" + mFallbackRestrictionsUid + + " " + ms + " ms"); + } else if (mFallbackRestrictionsTime + PrivacyManager.cRestrictionCacheTimeoutMs < now) { + // Check update + mFallbackRestrictions.reload(); + mFallbackRestrictionsUid = uid; + mFallbackRestrictionsTime = now; + long ms = System.currentTimeMillis() - now; + Util.log(null, Log.INFO, "Reload fallback restrictions uid=" + uid + " " + ms + " ms"); + } + } + + return getRestricted(restrictionName, methodName, mFallbackRestrictions); + } catch (Throwable ex) { + Util.bug(hook, ex); + return false; + } + } + + public static String getSettingFallback(String name, String defaultValue, boolean log) { + try { + long now = new Date().getTime(); + File file = new File(getPrefFileName(PREF_SETTINGS)); + if (!file.exists()) + if (log) + Util.log(null, Log.INFO, "Not found file=" + file.getAbsolutePath()); + + synchronized (mFallbackSettingsLock) { + // Initial load + if (mFallbackSettings == null) { + mFallbackSettings = new SharedPreferencesEx(file); + mFallbackSettingsTime = now; + if (log) { + long ms = System.currentTimeMillis() - now; + Util.log(null, Log.INFO, "Load fallback settings uid=" + Binder.getCallingUid() + " " + ms + + " ms"); + } + } + + // Get update + if (mFallbackSettingsTime + PrivacyManager.cSettingCacheTimeoutMs < now) { + mFallbackSettings.reload(); + mFallbackSettingsTime = now; + if (log) { + long ms = System.currentTimeMillis() - now; + Util.log(null, Log.INFO, "Reload fallback settings uid=" + Binder.getCallingUid() + " " + ms + + " ms"); + } + } + } + + return mFallbackSettings.getString(getSettingPref(name), defaultValue); + } catch (Throwable ex) { + if (log) + Util.bug(null, ex); + return defaultValue; + } + } + + public static void flush() { + Util.log(null, Log.INFO, "Flush uid=" + Binder.getCallingUid()); + synchronized (mFallbackRestrictionLock) { + mFallbackRestrictions = null; + } + synchronized (mFallbackSettingsLock) { + mFallbackSettings = null; + } + } + + // Helper methods + + private void enforcePermission() throws SecurityException { + if (Binder.getCallingUid() != Process.myUid()) + throw new SecurityException(); + } + + private static String getPrefFileName(String preference) { + return Util.getUserDataDirectory(Process.myUid()) + File.separator + "shared_prefs" + File.separator + + preference + ".xml"; + } + + private static String getPrefFileName(String preference, int uid) { + return Util.getUserDataDirectory(uid) + File.separator + "shared_prefs" + File.separator + preference + "." + + uid + ".xml"; + } + + private static void setPrefFileReadable(String preference) { + new File(getPrefFileName(preference)).setReadable(true, false); + } + + private static void setPrefFileReadable(String preference, int uid) { + new File(getPrefFileName(preference, uid)).setReadable(true, false); + } + + public static void fixFilePermissions() { + File folder = new File(Util.getUserDataDirectory(Process.myUid()) + File.separator + "shared_prefs"); + folder.setReadable(true, false); + File[] files = folder.listFiles(); + if (files != null) + for (File file : files) + if (file.getName().startsWith("biz.bokhorst.xprivacy.provider.") && file.getName().endsWith(".xml") + && !file.getName().contains(".usage.")) + file.setReadable(true, false); + } + + private static String getRestrictionPref(String restrictionName) { + return String.format("%s.%s", COL_RESTRICTED, restrictionName); + } + + private static String getExceptionPref(String restrictionName, String methodName) { + return String.format("%s.%s.%s", COL_METHOD, restrictionName, methodName); + } + + private static String getUsagePref(int uid, String methodName) { + return String.format("%s.%d.%s", COL_USED, uid, methodName); + } + + private static String getSettingPref(String settingName) { + return String.format("%s.%s", COL_SETTING, settingName); + } + + private static String getSettingName(String settingKey) { + return settingKey.substring(COL_SETTING.length() + 1); + } + + private static void convertRestrictions(Context context) throws IOException { + File source = new File(Util.getUserDataDirectory(Process.myUid()) + File.separator + "shared_prefs" + + File.separator + "biz.bokhorst.xprivacy.provider.xml"); + File backup = new File(source.getAbsoluteFile() + ".orig"); + if (source.exists() && !backup.exists()) { + Util.log(null, Log.WARN, "Converting restrictions"); + SharedPreferences prefs = context.getSharedPreferences(PREF_RESTRICTION, Context.MODE_WORLD_READABLE); + for (String key : prefs.getAll().keySet()) { + String[] component = key.split("\\."); + if (key.startsWith(COL_RESTRICTED)) { + String restrictionName = component[1]; + String value = prefs.getString(key, null); + List listRestriction = new ArrayList(Arrays.asList(value.split(","))); + listRestriction.remove(0); + for (String uid : listRestriction) + updateRestriction(context, Integer.parseInt(uid), restrictionName, null, false); + } else if (key.startsWith(COL_METHOD)) { + int uid = Integer.parseInt(component[1]); + String restrictionName = component[2]; + String methodName = component[3]; + boolean value = prefs.getBoolean(key, false); + updateRestriction(context, uid, restrictionName, methodName, value); + } else + Util.log(null, Log.WARN, "Unknown key=" + key); + } + + // Backup old file + Util.log(null, Log.INFO, "Backup name=" + backup.getAbsolutePath()); + Util.copy(source, backup); + } + } + + private static void convertSettings(Context context) throws IOException { + if (new File(getPrefFileName(PREF_SETTINGS)).exists()) { + SharedPreferences prefs = context.getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); + SharedPreferences.Editor editor = prefs.edit(); + for (String key : prefs.getAll().keySet()) + try { + String value = prefs.getString(key, null); + if (PrivacyManager.cValueRandomLegacy.equals(value)) + editor.putString(key, PrivacyManager.cValueRandom); + } catch (Throwable ex) { + } + + editor.commit(); + setPrefFileReadable(PREF_SETTINGS); + } + } + + private static void splitSettings(Context context) { + File prefFile = new File(getPrefFileName(PREF_SETTINGS)); + File migratedFile = new File(prefFile + ".migrated"); + if (prefFile.exists() && !migratedFile.exists()) { + Util.log(null, Log.WARN, "Splitting " + prefFile); + + SharedPreferences prefs = context.getSharedPreferences(PREF_SETTINGS, Context.MODE_WORLD_READABLE); + for (String settingKey : prefs.getAll().keySet()) + try { + int uid = 0; + String name = getSettingName(settingKey); + String value = prefs.getString(settingKey, ""); + + // Decode setting + String[] component = name.split("\\."); + if (name.startsWith("Account.") || name.startsWith("Application.") || name.startsWith("Contact.")) { + try { + // name.uid.key + uid = Integer.parseInt(component[1]); + name = component[0]; + for (int i = 2; i < component.length; i++) + name += "." + component[i]; + } catch (NumberFormatException ignored) { + // Initial uid/name will be used + } + } else if (component.length > 1) { + try { + // name.x.y.z.uid + uid = Integer.parseInt(component[component.length - 1]); + name = component[0]; + for (int i = 1; i < component.length - 1; i++) + name += "." + component[i]; + } catch (NumberFormatException ignored) { + // Initial uid/name will be used + } + } + + SharedPreferences aprefs = context.getSharedPreferences(PREF_SETTINGS + "." + uid, + Context.MODE_WORLD_READABLE); + SharedPreferences.Editor editor = aprefs.edit(); + editor.putString(name, value); + editor.commit(); + } catch (Throwable ex) { + // Legacy boolean + Util.bug(null, ex); + } + + prefFile.renameTo(migratedFile); + } + } + + // Migration + + public static void migrateLegacy(Context context) throws IOException { + convertSettings(context); + convertRestrictions(context); + splitSettings(context); + } + + public static List migrateRestrictions(Context context, int uid) { + List listWork = new ArrayList(); + + File prefFile = new File(getPrefFileName(PREF_RESTRICTION, uid)); + File migratedFile = new File(prefFile + ".migrated"); + if (prefFile.exists() && !migratedFile.exists()) { + Util.log(null, Log.WARN, "Migrating " + prefFile); + + SharedPreferences prefs = context.getSharedPreferences(PREF_RESTRICTION + "." + uid, + Context.MODE_WORLD_READABLE); + + // Process restrictions + for (String restrictionName : PrivacyManager.getRestrictions()) + if (getRestricted(restrictionName, null, prefs)) { + // Category + listWork.add(new PRestriction(uid, restrictionName, null, true)); + + // Exceptions + for (Hook md : PrivacyManager.getHooks(restrictionName, null)) { + boolean restricted = getRestricted(restrictionName, md.getName(), prefs); + if (!restricted || md.isDangerous()) + listWork.add(new PRestriction(uid, restrictionName, md.getName(), restricted)); + } + } + } + + return listWork; + } + + public static void finishMigrateRestrictions(int uid) { + File prefFile = new File(getPrefFileName(PREF_RESTRICTION, uid)); + File migratedFile = new File(prefFile + ".migrated"); + prefFile.renameTo(migratedFile); + } + + public static List migrateSettings(Context context, int uid) { + // Process settings + List listWork = new ArrayList(); + + File prefFile = new File(getPrefFileName(PREF_SETTINGS, uid)); + File migratedFile = new File(prefFile + ".migrated"); + if (prefFile.exists() && !migratedFile.exists()) { + Util.log(null, Log.WARN, "Migrating " + prefFile); + + SharedPreferences prefs = context.getSharedPreferences(PREF_SETTINGS + "." + uid, + Context.MODE_WORLD_READABLE); + for (String name : prefs.getAll().keySet()) + try { + String value = prefs.getString(name, null); + if (value != null && !"".equals(value)) { + String type; + if (name.startsWith("Account.") || name.startsWith("Application.") + || name.startsWith("Contact.") || name.startsWith("Template.")) { + int dot = name.indexOf('.'); + type = name.substring(0, dot); + name = name.substring(dot + 1); + } else + type = ""; + listWork.add(new PSetting(uid, type, name, value)); + } + } catch (Throwable ex) { + // Legacy boolean + Util.bug(null, ex); + } + } + + return listWork; + } + + public static void finishMigrateSettings(int uid) { + File prefFile = new File(getPrefFileName(PREF_SETTINGS, uid)); + File migratedFile = new File(prefFile + ".migrated"); + prefFile.renameTo(migratedFile); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyService.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyService.java new file mode 100644 index 0000000..79a8a7a --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/PrivacyService.java @@ -0,0 +1,2869 @@ +package biz.bokhorst.xprivacy; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationManager; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDoneException; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteStatement; +import android.graphics.PixelFormat; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Process; +import android.os.RemoteException; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.StrictMode.ThreadPolicy; +import android.support.v4.app.NotificationCompat; +import android.text.TextUtils; +import android.util.Log; +import android.util.Patterns; +import android.util.SparseArray; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.Spinner; +import android.widget.TableRow; +import android.widget.TextView; +import android.widget.Toast; + +public class PrivacyService extends IPrivacyService.Stub { + private static int mXUid = -1; + private static Object mAm; + private static Context mContext; + private static String mSecret = null; + private static Thread mWorker = null; + private static Handler mHandler = null; + private static long mOnDemandLastAnswer = 0; + private static Semaphore mOndemandSemaphore = new Semaphore(1, true); + private static List mListError = new ArrayList(); + private static IPrivacyService mClient = null; + + private static final String cTableRestriction = "restriction"; + private static final String cTableUsage = "usage"; + private static final String cTableSetting = "setting"; + + private static final int cCurrentVersion = 481; + private static final String cServiceName = "xprivacy481"; + + private boolean mCorrupt = false; + private boolean mNotified = false; + private SQLiteDatabase mDb = null; + private SQLiteDatabase mDbUsage = null; + private SQLiteStatement stmtGetRestriction = null; + private SQLiteStatement stmtGetSetting = null; + private SQLiteStatement stmtGetUsageRestriction = null; + private SQLiteStatement stmtGetUsageMethod = null; + private ReentrantReadWriteLock mLock = new ReentrantReadWriteLock(true); + private ReentrantReadWriteLock mLockUsage = new ReentrantReadWriteLock(true); + + private AtomicLong mCount = new AtomicLong(0); + private AtomicLong mRestricted = new AtomicLong(0); + + private Map mSettingCache = new HashMap(); + private Map mAskedOnceCache = new HashMap(); + private Map mRestrictionCache = new HashMap(); + + private final long cMaxUsageDataHours = 12; + private final int cMaxUsageDataCount = 700; + private final int cMaxOnDemandDialog = 20; // seconds + + private ExecutorService mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), + new PriorityThreadFactory()); + + final class PriorityThreadFactory implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + } + + // sqlite3 /data/system/xprivacy/xprivacy.db + + private PrivacyService() { + } + + private static PrivacyService mPrivacyService = null; + + private static String getServiceName() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? "user." : "") + cServiceName; + } + + public static void register(List listError, ClassLoader classLoader, String secret, Object am) { + // Store secret and errors + mAm = am; + mSecret = secret; + mListError.addAll(listError); + + try { + // Register privacy service + mPrivacyService = new PrivacyService(); + + // @formatter:off + // public static void addService(String name, IBinder service) + // public static void addService(String name, IBinder service, boolean allowIsolated) + // @formatter:on + + // Requires this in /service_contexts + // xprivacy453 u:object_r:system_server_service:s0 + + Class cServiceManager = Class.forName("android.os.ServiceManager", false, classLoader); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + Method mAddService = cServiceManager.getDeclaredMethod("addService", String.class, IBinder.class, + boolean.class); + mAddService.invoke(null, getServiceName(), mPrivacyService, true); + } else { + Method mAddService = cServiceManager.getDeclaredMethod("addService", String.class, IBinder.class); + mAddService.invoke(null, getServiceName(), mPrivacyService); + } + + // This will and should open the database + Util.log(null, Log.WARN, "Service registered name=" + getServiceName() + " version=" + cCurrentVersion); + + // Publish semaphore to activity manager service + XActivityManagerService.setSemaphore(mOndemandSemaphore); + + // Get context + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Field fContext = null; + Class cam = am.getClass(); + while (cam != null && fContext == null) + try { + fContext = cam.getDeclaredField("mContext"); + } catch (NoSuchFieldException ignored) { + cam = cam.getSuperclass(); + } + + if (fContext == null) + Util.log(null, Log.ERROR, am.getClass().getName() + ".mContext not found"); + else { + fContext.setAccessible(true); + mContext = (Context) fContext.get(am); + } + } + + // Start a worker thread + mWorker = new Thread(new Runnable() { + @Override + public void run() { + try { + Looper.prepare(); + mHandler = new Handler(); + Looper.loop(); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + mWorker.start(); + + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public static boolean isRegistered() { + return (mPrivacyService != null); + } + + public static boolean checkClient() { + // Runs client side + try { + IPrivacyService client = getClient(); + if (client != null) + return (client.getVersion() == cCurrentVersion); + } catch (SecurityException ignored) { + } catch (Throwable ex) { + Util.bug(null, ex); + } + return false; + } + + public static IPrivacyService getClient() { + // Runs client side + if (mClient == null) + try { + // public static IBinder getService(String name) + Class cServiceManager = Class.forName("android.os.ServiceManager"); + Method mGetService = cServiceManager.getDeclaredMethod("getService", String.class); + mClient = IPrivacyService.Stub.asInterface((IBinder) mGetService.invoke(null, getServiceName())); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + return mClient; + } + + public static void reportErrorInternal(String message) { + synchronized (mListError) { + mListError.add(message); + } + } + + public static PRestriction getRestrictionProxy(final PRestriction restriction, boolean usage, String secret) + throws RemoteException { + if (isRegistered()) + return mPrivacyService.getRestriction(restriction, usage, secret); + else { + IPrivacyService client = getClient(); + if (client == null) { + Log.w("XPrivacy", "No client for " + restriction); + PRestriction result = new PRestriction(restriction); + result.restricted = false; + return result; + } else + return client.getRestriction(restriction, usage, secret); + } + } + + public static PSetting getSettingProxy(PSetting setting) throws RemoteException { + if (isRegistered()) + return mPrivacyService.getSetting(setting); + else { + IPrivacyService client = getClient(); + if (client == null) { + Log.w("XPrivacy", "No client for " + setting + " uid=" + Process.myUid() + " pid=" + Process.myPid()); + Log.w("XPrivacy", Log.getStackTraceString(new Exception("StackTrace"))); + return setting; + } else + return client.getSetting(setting); + } + } + + // Management + + @Override + public int getVersion() throws RemoteException { + enforcePermission(-1); + return cCurrentVersion; + } + + @Override + public List check() throws RemoteException { + enforcePermission(-1); + + List listError = new ArrayList(); + synchronized (mListError) { + int c = 0; + int i = 0; + while (i < mListError.size()) { + String msg = mListError.get(i); + c += msg.length(); + if (c < 10000) + listError.add(msg); + else + break; + i++; + } + } + + return listError; + } + + @Override + public boolean databaseCorrupt() { + return mCorrupt; + } + + @Override + public void reportError(String message) throws RemoteException { + reportErrorInternal(message); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Map getStatistics() throws RemoteException { + Map map = new HashMap(); + map.put("restriction_count", mCount.longValue()); + map.put("restriction_restricted", mRestricted.longValue()); + map.put("uptime_milliseconds", SystemClock.elapsedRealtime()); + return map; + }; + + // Restrictions + + @Override + public void setRestriction(PRestriction restriction) throws RemoteException { + try { + enforcePermission(restriction.uid); + setRestrictionInternal(restriction); + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + private void setRestrictionInternal(PRestriction restriction) throws RemoteException { + // Validate + if (restriction.restrictionName == null) { + Util.log(null, Log.ERROR, "Set invalid restriction " + restriction); + Util.logStack(null, Log.ERROR); + throw new RemoteException("Invalid restriction"); + } + + try { + SQLiteDatabase db = getDb(); + if (db == null) + return; + // 0 not restricted, ask + // 1 restricted, ask + // 2 not restricted, asked + // 3 restricted, asked + + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + // Create category record + if (restriction.methodName == null) { + ContentValues cvalues = new ContentValues(); + cvalues.put("uid", restriction.uid); + cvalues.put("restriction", restriction.restrictionName); + cvalues.put("method", ""); + cvalues.put("restricted", (restriction.restricted ? 1 : 0) + (restriction.asked ? 2 : 0)); + db.insertWithOnConflict(cTableRestriction, null, cvalues, SQLiteDatabase.CONFLICT_REPLACE); + } + + // Create method exception record + if (restriction.methodName != null) { + ContentValues mvalues = new ContentValues(); + mvalues.put("uid", restriction.uid); + mvalues.put("restriction", restriction.restrictionName); + mvalues.put("method", restriction.methodName); + mvalues.put("restricted", (restriction.restricted ? 0 : 1) + (restriction.asked ? 2 : 0)); + db.insertWithOnConflict(cTableRestriction, null, mvalues, SQLiteDatabase.CONFLICT_REPLACE); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + + // Update cache + synchronized (mRestrictionCache) { + for (CRestriction key : new ArrayList(mRestrictionCache.keySet())) + if (key.isSameMethod(restriction)) + mRestrictionCache.remove(key); + + CRestriction key = new CRestriction(restriction, restriction.extra); + if (mRestrictionCache.containsKey(key)) + mRestrictionCache.remove(key); + mRestrictionCache.put(key, key); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + @Override + public void setRestrictionList(List listRestriction) throws RemoteException { + int uid = -1; + for (PRestriction restriction : listRestriction) + if (uid < 0) + uid = restriction.uid; + else if (uid != restriction.uid) + throw new SecurityException(); + enforcePermission(uid); + for (PRestriction restriction : listRestriction) + setRestrictionInternal(restriction); + } + + @Override + public PRestriction getRestriction(final PRestriction restriction, boolean usage, String secret) + throws RemoteException { + long start = System.currentTimeMillis(); + + // Translate isolated uid + restriction.uid = getIsolatedUid(restriction.uid); + + boolean ccached = false; + boolean mcached = false; + int userId = Util.getUserId(restriction.uid); + final PRestriction mresult = new PRestriction(restriction); + + // Disable strict mode + ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + ThreadPolicy newPolicy = new ThreadPolicy.Builder(oldPolicy).permitDiskReads().permitDiskWrites().build(); + StrictMode.setThreadPolicy(newPolicy); + + try { + // No permissions enforced, but usage data requires a secret + + // Sanity checks + if (restriction.restrictionName == null) { + Util.log(null, Log.ERROR, "Get invalid restriction " + restriction); + return mresult; + } + if (usage && restriction.methodName == null) { + Util.log(null, Log.ERROR, "Get invalid restriction " + restriction); + return mresult; + } + + // Get meta data + Hook hook = null; + if (restriction.methodName != null) { + hook = PrivacyManager.getHook(restriction.restrictionName, restriction.methodName); + if (hook == null) + // Can happen after updating + Util.log(null, Log.WARN, "Hook not found in service: " + restriction); + else if (hook.getFrom() != null) { + String version = getSetting(new PSetting(userId, "", PrivacyManager.cSettingVersion, null)).value; + if (version != null && new Version(version).compareTo(hook.getFrom()) < 0) + if (hook.getReplacedRestriction() == null) { + Util.log(null, Log.WARN, "Disabled version=" + version + " from=" + hook.getFrom() + + " hook=" + hook); + return mresult; + } else { + restriction.restrictionName = hook.getReplacedRestriction(); + restriction.methodName = hook.getReplacedMethod(); + Util.log(null, Log.WARN, "Checking " + restriction + " instead of " + hook); + } + } + } + + // Process IP address + if (restriction.extra != null && Meta.cTypeIPAddress.equals(hook.whitelist())) { + int colon = restriction.extra.lastIndexOf(':'); + String address = (colon >= 0 ? restriction.extra.substring(0, colon) : restriction.extra); + String port = (colon >= 0 ? restriction.extra.substring(colon) : ""); + + int slash = address.indexOf('/'); + if (slash == 0) // IP address + restriction.extra = address.substring(slash + 1) + port; + else if (slash > 0) // Domain name + restriction.extra = address.substring(0, slash) + port; + } + + // Check for system component + if (!PrivacyManager.isApplication(restriction.uid)) + if (!getSettingBool(userId, PrivacyManager.cSettingSystem, false)) + return mresult; + + // Check if restrictions enabled + if (usage && !getSettingBool(restriction.uid, PrivacyManager.cSettingRestricted, true)) + return mresult; + + // Check if can be restricted + if (!PrivacyManager.canRestrict(restriction.uid, getXUid(), restriction.restrictionName, + restriction.methodName, false)) + mresult.asked = true; + else { + // Check cache for method + CRestriction key = new CRestriction(restriction, restriction.extra); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(key)) { + mcached = true; + CRestriction cache = mRestrictionCache.get(key); + mresult.restricted = cache.restricted; + mresult.asked = cache.asked; + } + } + + if (!mcached) { + boolean methodFound = false; + PRestriction cresult = new PRestriction(restriction.uid, restriction.restrictionName, null); + + // Check cache for category + CRestriction ckey = new CRestriction(cresult, null); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(ckey)) { + ccached = true; + CRestriction crestriction = mRestrictionCache.get(ckey); + cresult.restricted = crestriction.restricted; + cresult.asked = crestriction.asked; + mresult.restricted = cresult.restricted; + mresult.asked = cresult.asked; + } + } + + // Get database reference + SQLiteDatabase db = getDb(); + if (db == null) + return mresult; + + // Precompile statement when needed + if (stmtGetRestriction == null) { + String sql = "SELECT restricted FROM " + cTableRestriction + + " WHERE uid=? AND restriction=? AND method=?"; + stmtGetRestriction = db.compileStatement(sql); + } + + // Execute statement + mLock.readLock().lock(); + try { + db.beginTransaction(); + try { + if (!ccached) + try { + synchronized (stmtGetRestriction) { + stmtGetRestriction.clearBindings(); + stmtGetRestriction.bindLong(1, restriction.uid); + stmtGetRestriction.bindString(2, restriction.restrictionName); + stmtGetRestriction.bindString(3, ""); + long state = stmtGetRestriction.simpleQueryForLong(); + cresult.restricted = ((state & 1) != 0); + cresult.asked = ((state & 2) != 0); + mresult.restricted = cresult.restricted; + mresult.asked = cresult.asked; + } + } catch (SQLiteDoneException ignored) { + } + + if (restriction.methodName != null) + try { + synchronized (stmtGetRestriction) { + stmtGetRestriction.clearBindings(); + stmtGetRestriction.bindLong(1, restriction.uid); + stmtGetRestriction.bindString(2, restriction.restrictionName); + stmtGetRestriction.bindString(3, restriction.methodName); + long state = stmtGetRestriction.simpleQueryForLong(); + // Method can be excepted + if (mresult.restricted) + mresult.restricted = ((state & 1) == 0); + // Category asked=true takes precedence + if (!mresult.asked) + mresult.asked = ((state & 2) != 0); + methodFound = true; + } + } catch (SQLiteDoneException ignored) { + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.readLock().unlock(); + } + + // Default dangerous + if (!methodFound && hook != null && hook.isDangerous()) + if (!getSettingBool(userId, PrivacyManager.cSettingDangerous, false)) { + if (mresult.restricted) + mresult.restricted = false; + if (!mresult.asked) + mresult.asked = (hook.whitelist() == null); + } + + // Check whitelist + if (usage && hook != null && hook.whitelist() != null && restriction.extra != null) { + String value = getSetting(new PSetting(restriction.uid, hook.whitelist(), restriction.extra, + null)).value; + if (value == null) { + for (String xextra : getXExtra(restriction, hook)) { + value = getSetting(new PSetting(restriction.uid, hook.whitelist(), xextra, null)).value; + if (value != null) + break; + } + } + if (value != null) { + // true means allow, false means block + mresult.restricted = !Boolean.parseBoolean(value); + mresult.asked = true; + } + } + + // Fallback + if (!mresult.restricted && usage && PrivacyManager.isApplication(restriction.uid) + && !getSettingBool(userId, PrivacyManager.cSettingMigrated, false)) { + if (hook != null && !hook.isDangerous()) { + mresult.restricted = PrivacyProvider.getRestrictedFallback(null, restriction.uid, + restriction.restrictionName, restriction.methodName); + Util.log(null, Log.WARN, "Fallback " + mresult); + } + } + + // Update cache + CRestriction cukey = new CRestriction(cresult, null); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(cukey)) + mRestrictionCache.remove(cukey); + mRestrictionCache.put(cukey, cukey); + } + CRestriction ukey = new CRestriction(mresult, restriction.extra); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(ukey)) + mRestrictionCache.remove(ukey); + mRestrictionCache.put(ukey, ukey); + } + } + + // Ask to restrict + OnDemandResult oResult = new OnDemandResult(); + if (!mresult.asked && usage) { + oResult = onDemandDialog(hook, restriction, mresult); + + // Update cache + if (oResult.ondemand && !oResult.once) { + CRestriction okey = new CRestriction(mresult, oResult.whitelist ? restriction.extra : null); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(okey)) + mRestrictionCache.remove(okey); + mRestrictionCache.put(okey, okey); + } + } + } + + // Notify user + if (!oResult.ondemand && mresult.restricted && usage && hook != null && hook.shouldNotify()) { + notifyRestricted(restriction); + mresult.time = new Date().getTime(); + } + } + + // Store usage data + if (usage && hook != null) + storeUsageData(restriction, secret, mresult); + + } catch (SQLiteException ex) { + notifyException(ex); + } catch (Throwable ex) { + Util.bug(null, ex); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + + long ms = System.currentTimeMillis() - start; + Util.log( + null, + ms < PrivacyManager.cWarnServiceDelayMs ? Log.INFO : Log.WARN, + String.format("Get service %s%s %d ms", restriction, (ccached ? " (ccached)" : "") + + (mcached ? " (mcached)" : ""), ms)); + + if (mresult.debug) + Util.logStack(null, Log.WARN); + if (usage) { + mCount.incrementAndGet(); + if (mresult.restricted) + mRestricted.incrementAndGet(); + } + + return mresult; + } + + private void storeUsageData(final PRestriction restriction, String secret, final PRestriction mresult) + throws RemoteException { + // Check if enabled + final int userId = Util.getUserId(restriction.uid); + if (getSettingBool(userId, PrivacyManager.cSettingUsage, true) + && !getSettingBool(restriction.uid, PrivacyManager.cSettingNoUsageData, false)) { + // Check secret + boolean allowed = true; + if (Util.getAppId(Binder.getCallingUid()) != getXUid()) { + if (mSecret == null || !mSecret.equals(secret)) { + allowed = false; + Util.log(null, Log.WARN, "Invalid secret restriction=" + restriction); + } + } + + if (allowed) { + mExecutor.execute(new Runnable() { + public void run() { + try { + if (XActivityManagerService.canWriteUsageData()) { + SQLiteDatabase dbUsage = getDbUsage(); + if (dbUsage == null) + return; + + // Parameter + String extra = ""; + if (restriction.extra != null) + if (getSettingBool(userId, PrivacyManager.cSettingParameters, false)) + extra = restriction.extra; + + // Value + if (restriction.value != null) + if (!getSettingBool(userId, PrivacyManager.cSettingValues, false)) + restriction.value = null; + + mLockUsage.writeLock().lock(); + try { + dbUsage.beginTransaction(); + try { + ContentValues values = new ContentValues(); + values.put("uid", restriction.uid); + values.put("restriction", restriction.restrictionName); + values.put("method", restriction.methodName); + values.put("restricted", mresult.restricted); + values.put("time", new Date().getTime()); + values.put("extra", extra); + if (restriction.value == null) + values.putNull("value"); + else + values.put("value", restriction.value); + dbUsage.insertWithOnConflict(cTableUsage, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.writeLock().unlock(); + } + } + } catch (SQLiteException ex) { + Util.log(null, Log.WARN, ex.toString()); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + } + } + } + + @Override + public List getRestrictionList(PRestriction selector) throws RemoteException { + List result = new ArrayList(); + try { + enforcePermission(selector.uid); + + PRestriction query; + if (selector.restrictionName == null) + for (String sRestrictionName : PrivacyManager.getRestrictions()) { + PRestriction restriction = new PRestriction(selector.uid, sRestrictionName, null, false); + query = getRestriction(restriction, false, null); + restriction.restricted = query.restricted; + restriction.asked = query.asked; + result.add(restriction); + } + else + for (Hook md : PrivacyManager.getHooks(selector.restrictionName, null)) { + PRestriction restriction = new PRestriction(selector.uid, selector.restrictionName, md.getName(), + false); + query = getRestriction(restriction, false, null); + restriction.restricted = query.restricted; + restriction.asked = query.asked; + result.add(restriction); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + return result; + } + + @Override + public boolean isRestrictionSet(PRestriction restriction) throws RemoteException { + try { + // No permissions required + boolean set = false; + + SQLiteDatabase db = getDb(); + if (db != null) { + // Precompile statement when needed + if (stmtGetRestriction == null) { + String sql = "SELECT restricted FROM " + cTableRestriction + + " WHERE uid=? AND restriction=? AND method=?"; + stmtGetRestriction = db.compileStatement(sql); + } + + // Execute statement + mLock.readLock().lock(); + try { + db.beginTransaction(); + try { + try { + synchronized (stmtGetRestriction) { + stmtGetRestriction.clearBindings(); + stmtGetRestriction.bindLong(1, restriction.uid); + stmtGetRestriction.bindString(2, restriction.restrictionName); + stmtGetRestriction.bindString(3, restriction.methodName); + stmtGetRestriction.simpleQueryForLong(); + set = true; + } + } catch (SQLiteDoneException ignored) { + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.readLock().unlock(); + } + } + + return set; + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + @Override + public void deleteRestrictions(int uid, String restrictionName) throws RemoteException { + try { + enforcePermission(uid); + SQLiteDatabase db = getDb(); + if (db == null) + return; + + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + if ("".equals(restrictionName)) + db.delete(cTableRestriction, "uid=?", new String[] { Integer.toString(uid) }); + else + db.delete(cTableRestriction, "uid=? AND restriction=?", new String[] { Integer.toString(uid), + restrictionName }); + Util.log(null, Log.WARN, "Restrictions deleted uid=" + uid + " category=" + restrictionName); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + + // Clear caches + synchronized (mRestrictionCache) { + mRestrictionCache.clear(); + } + synchronized (mAskedOnceCache) { + mAskedOnceCache.clear(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + // Usage + + @Override + public long getUsage(List listRestriction) throws RemoteException { + long lastUsage = 0; + try { + int uid = -1; + for (PRestriction restriction : listRestriction) + if (uid < 0) + uid = restriction.uid; + else if (uid != restriction.uid) + throw new SecurityException(); + enforcePermission(uid); + SQLiteDatabase dbUsage = getDbUsage(); + + // Precompile statement when needed + if (stmtGetUsageRestriction == null) { + String sql = "SELECT MAX(time) FROM " + cTableUsage + " WHERE uid=? AND restriction=?"; + stmtGetUsageRestriction = dbUsage.compileStatement(sql); + } + if (stmtGetUsageMethod == null) { + String sql = "SELECT MAX(time) FROM " + cTableUsage + " WHERE uid=? AND restriction=? AND method=?"; + stmtGetUsageMethod = dbUsage.compileStatement(sql); + } + + mLockUsage.readLock().lock(); + try { + dbUsage.beginTransaction(); + try { + for (PRestriction restriction : listRestriction) { + if (restriction.methodName == null) + try { + synchronized (stmtGetUsageRestriction) { + stmtGetUsageRestriction.clearBindings(); + stmtGetUsageRestriction.bindLong(1, restriction.uid); + stmtGetUsageRestriction.bindString(2, restriction.restrictionName); + lastUsage = Math.max(lastUsage, stmtGetUsageRestriction.simpleQueryForLong()); + } + } catch (SQLiteDoneException ignored) { + } + else + try { + synchronized (stmtGetUsageMethod) { + stmtGetUsageMethod.clearBindings(); + stmtGetUsageMethod.bindLong(1, restriction.uid); + stmtGetUsageMethod.bindString(2, restriction.restrictionName); + stmtGetUsageMethod.bindString(3, restriction.methodName); + lastUsage = Math.max(lastUsage, stmtGetUsageMethod.simpleQueryForLong()); + } + } catch (SQLiteDoneException ignored) { + } + } + + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.readLock().unlock(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + return lastUsage; + } + + @Override + public List getUsageList(int uid, String restrictionName) throws RemoteException { + List result = new ArrayList(); + try { + enforcePermission(-1); + SQLiteDatabase dbUsage = getDbUsage(); + int userId = Util.getUserId(Binder.getCallingUid()); + + mLockUsage.readLock().lock(); + try { + dbUsage.beginTransaction(); + try { + String sFrom = Long.toString(new Date().getTime() - cMaxUsageDataHours * 60L * 60L * 1000L); + Cursor cursor; + if (uid == 0) { + if ("".equals(restrictionName)) + cursor = dbUsage.query(cTableUsage, new String[] { "uid", "restriction", "method", + "restricted", "time", "extra", "value" }, "time>?", new String[] { sFrom }, null, + null, "time DESC"); + else + cursor = dbUsage.query(cTableUsage, new String[] { "uid", "restriction", "method", + "restricted", "time", "extra", "value" }, "restriction=? AND time>?", new String[] { + restrictionName, sFrom }, null, null, "time DESC"); + } else { + if ("".equals(restrictionName)) + cursor = dbUsage.query(cTableUsage, new String[] { "uid", "restriction", "method", + "restricted", "time", "extra", "value" }, "uid=? AND time>?", new String[] { + Integer.toString(uid), sFrom }, null, null, "time DESC"); + else + cursor = dbUsage.query(cTableUsage, new String[] { "uid", "restriction", "method", + "restricted", "time", "extra", "value" }, "uid=? AND restriction=? AND time>?", + new String[] { Integer.toString(uid), restrictionName, sFrom }, null, null, + "time DESC"); + } + + if (cursor == null) + Util.log(null, Log.WARN, "Database cursor null (usage data)"); + else + try { + int count = 0; + while (count++ < cMaxUsageDataCount && cursor.moveToNext()) { + PRestriction data = new PRestriction(); + data.uid = cursor.getInt(0); + data.restrictionName = cursor.getString(1); + data.methodName = cursor.getString(2); + data.restricted = (cursor.getInt(3) > 0); + data.time = cursor.getLong(4); + data.extra = cursor.getString(5); + if (cursor.isNull(6)) + data.value = null; + else + data.value = cursor.getString(6); + if (userId == 0 || Util.getUserId(data.uid) == userId) + result.add(data); + } + } finally { + cursor.close(); + } + + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.readLock().unlock(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + return result; + } + + @Override + public void deleteUsage(int uid) throws RemoteException { + try { + enforcePermission(uid); + SQLiteDatabase dbUsage = getDbUsage(); + + mLockUsage.writeLock().lock(); + try { + dbUsage.beginTransaction(); + try { + if (uid == 0) + dbUsage.delete(cTableUsage, null, new String[] {}); + else + dbUsage.delete(cTableUsage, "uid=?", new String[] { Integer.toString(uid) }); + Util.log(null, Log.WARN, "Usage data deleted uid=" + uid); + + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.writeLock().unlock(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + // Settings + + @Override + public void setSetting(PSetting setting) throws RemoteException { + try { + enforcePermission(setting.uid); + setSettingInternal(setting); + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + private void setSettingInternal(PSetting setting) throws RemoteException { + try { + SQLiteDatabase db = getDb(); + if (db == null) + return; + + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + if (setting.value == null) + db.delete(cTableSetting, "uid=? AND type=? AND name=?", + new String[] { Integer.toString(setting.uid), setting.type, setting.name }); + else { + // Create record + ContentValues values = new ContentValues(); + values.put("uid", setting.uid); + values.put("type", setting.type); + values.put("name", setting.name); + values.put("value", setting.value); + + // Insert/update record + db.insertWithOnConflict(cTableSetting, null, values, SQLiteDatabase.CONFLICT_REPLACE); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + + if (PrivacyManager.cSettingAOSPMode.equals(setting.name)) + if (setting.value == null || Boolean.toString(false).equals(setting.value)) + new File("/data/system/xprivacy/aosp").delete(); + else + new File("/data/system/xprivacy/aosp").createNewFile(); + + // Update cache + CSetting key = new CSetting(setting.uid, setting.type, setting.name); + key.setValue(setting.value); + synchronized (mSettingCache) { + if (mSettingCache.containsKey(key)) + mSettingCache.remove(key); + if (setting.value != null) + mSettingCache.put(key, key); + } + + // Clear restrictions for white list + if (Meta.isWhitelist(setting.type)) + for (String restrictionName : PrivacyManager.getRestrictions()) + for (Hook hook : PrivacyManager.getHooks(restrictionName, null)) + if (setting.type.equals(hook.whitelist())) { + PRestriction restriction = new PRestriction(setting.uid, hook.getRestrictionName(), + hook.getName()); + Util.log(null, Log.WARN, "Clearing cache for " + restriction); + synchronized (mRestrictionCache) { + for (CRestriction mkey : new ArrayList(mRestrictionCache.keySet())) + if (mkey.isSameMethod(restriction)) { + Util.log(null, Log.WARN, "Removing " + mkey); + mRestrictionCache.remove(mkey); + } + } + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + @Override + public void setSettingList(List listSetting) throws RemoteException { + int uid = -1; + for (PSetting setting : listSetting) + if (uid < 0) + uid = setting.uid; + else if (uid != setting.uid) + throw new SecurityException(); + enforcePermission(uid); + for (PSetting setting : listSetting) + setSettingInternal(setting); + } + + @Override + public PSetting getSetting(PSetting setting) throws RemoteException { + long start = System.currentTimeMillis(); + + // Translate isolated uid + setting.uid = getIsolatedUid(setting.uid); + + int userId = Util.getUserId(setting.uid); + + // Special case + if (Meta.cTypeAccountHash.equals(setting.type)) + try { + setting.type = Meta.cTypeAccount; + setting.name = Util.sha1(setting.name); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + // Default result + PSetting result = new PSetting(setting.uid, setting.type, setting.name, setting.value); + + // Disable strict mode + ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + ThreadPolicy newPolicy = new ThreadPolicy.Builder(oldPolicy).permitDiskReads().permitDiskWrites().build(); + StrictMode.setThreadPolicy(newPolicy); + + try { + // No permissions enforced + + // Check cache + CSetting key = new CSetting(setting.uid, setting.type, setting.name); + synchronized (mSettingCache) { + if (mSettingCache.containsKey(key)) { + result.value = mSettingCache.get(key).getValue(); + if (result.value == null) + result.value = setting.value; // default value + return result; + } + } + + // No persmissions required + SQLiteDatabase db = getDb(); + if (db == null) + return result; + + // Fallback + if (!PrivacyManager.cSettingMigrated.equals(setting.name) + && !getSettingBool(userId, PrivacyManager.cSettingMigrated, false)) { + if (setting.uid == 0) + result.value = PrivacyProvider.getSettingFallback(setting.name, null, false); + if (result.value == null) { + result.value = PrivacyProvider.getSettingFallback( + String.format("%s.%d", setting.name, setting.uid), setting.value, false); + return result; + } + } + + // Precompile statement when needed + if (stmtGetSetting == null) { + String sql = "SELECT value FROM " + cTableSetting + " WHERE uid=? AND type=? AND name=?"; + stmtGetSetting = db.compileStatement(sql); + } + + // Execute statement + boolean found = false; + mLock.readLock().lock(); + try { + db.beginTransaction(); + try { + try { + synchronized (stmtGetSetting) { + stmtGetSetting.clearBindings(); + stmtGetSetting.bindLong(1, setting.uid); + stmtGetSetting.bindString(2, setting.type); + stmtGetSetting.bindString(3, setting.name); + result.value = stmtGetSetting.simpleQueryForString(); + found = true; + } + } catch (SQLiteDoneException ignored) { + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.readLock().unlock(); + } + + // Add to cache + key.setValue(found ? result.value : null); + synchronized (mSettingCache) { + if (mSettingCache.containsKey(key)) + mSettingCache.remove(key); + mSettingCache.put(key, key); + } + + // Default value + if (result.value == null) + result.value = setting.value; + + } catch (SQLiteException ex) { + notifyException(ex); + } catch (Throwable ex) { + Util.bug(null, ex); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + + long ms = System.currentTimeMillis() - start; + Util.log(null, ms < PrivacyManager.cWarnServiceDelayMs ? Log.INFO : Log.WARN, + String.format("Get service %s %d ms", setting, ms)); + + return result; + } + + @Override + public List getSettingList(PSetting selector) throws RemoteException { + List listSetting = new ArrayList(); + try { + enforcePermission(selector.uid); + SQLiteDatabase db = getDb(); + if (db == null) + return listSetting; + + mLock.readLock().lock(); + try { + db.beginTransaction(); + try { + Cursor cursor; + if (selector.type == null) + cursor = db.query(cTableSetting, new String[] { "type", "name", "value" }, "uid=?", + new String[] { Integer.toString(selector.uid) }, null, null, null); + else + cursor = db.query(cTableSetting, new String[] { "type", "name", "value" }, "uid=? AND type=?", + new String[] { Integer.toString(selector.uid), selector.type }, null, null, null); + if (cursor == null) + Util.log(null, Log.WARN, "Database cursor null (settings)"); + else + try { + while (cursor.moveToNext()) + listSetting.add(new PSetting(selector.uid, cursor.getString(0), cursor.getString(1), + cursor.getString(2))); + } finally { + cursor.close(); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.readLock().unlock(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + return listSetting; + } + + @Override + public void deleteSettings(int uid) throws RemoteException { + try { + enforcePermission(uid); + SQLiteDatabase db = getDb(); + if (db == null) + return; + + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.delete(cTableSetting, "uid=?", new String[] { Integer.toString(uid) }); + Util.log(null, Log.WARN, "Settings deleted uid=" + uid); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + + // Clear cache + synchronized (mSettingCache) { + mSettingCache.clear(); + } + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + @Override + public void clear() throws RemoteException { + try { + enforcePermission(0); + SQLiteDatabase db = getDb(); + SQLiteDatabase dbUsage = getDbUsage(); + if (db == null || dbUsage == null) + return; + + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DELETE FROM " + cTableRestriction); + db.execSQL("DELETE FROM " + cTableSetting); + Util.log(null, Log.WARN, "Database cleared"); + + // Reset migrated + ContentValues values = new ContentValues(); + values.put("uid", 0); + values.put("type", ""); + values.put("name", PrivacyManager.cSettingMigrated); + values.put("value", Boolean.toString(true)); + db.insertWithOnConflict(cTableSetting, null, values, SQLiteDatabase.CONFLICT_REPLACE); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + + // Clear caches + synchronized (mRestrictionCache) { + mRestrictionCache.clear(); + } + synchronized (mSettingCache) { + mSettingCache.clear(); + } + synchronized (mAskedOnceCache) { + mAskedOnceCache.clear(); + } + Util.log(null, Log.WARN, "Caches cleared"); + + mLockUsage.writeLock().lock(); + try { + dbUsage.beginTransaction(); + try { + dbUsage.execSQL("DELETE FROM " + cTableUsage); + Util.log(null, Log.WARN, "Usage database cleared"); + + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.writeLock().unlock(); + } + + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + @Override + public void flush() throws RemoteException { + try { + enforcePermission(0); + synchronized (mRestrictionCache) { + mRestrictionCache.clear(); + } + synchronized (mAskedOnceCache) { + mAskedOnceCache.clear(); + } + synchronized (mSettingCache) { + mSettingCache.clear(); + } + Util.log(null, Log.WARN, "Service cache flushed"); + } catch (Throwable ex) { + Util.bug(null, ex); + throw new RemoteException(ex.toString()); + } + } + + @Override + public void dump(int uid) throws RemoteException { + if (uid == 0) { + + } else { + synchronized (mRestrictionCache) { + for (CRestriction crestriction : mRestrictionCache.keySet()) + if (crestriction.getUid() == uid) + Util.log(null, Log.WARN, "Dump crestriction=" + crestriction); + } + synchronized (mAskedOnceCache) { + for (CRestriction crestriction : mAskedOnceCache.keySet()) + if (crestriction.getUid() == uid && !crestriction.isExpired()) + Util.log(null, Log.WARN, "Dump asked=" + crestriction); + } + synchronized (mSettingCache) { + for (CSetting csetting : mSettingCache.keySet()) + if (csetting.getUid() == uid) + Util.log(null, Log.WARN, "Dump csetting=" + csetting); + } + } + } + + // Helper classes + + final class OnDemandResult { + public boolean ondemand = false; + public boolean once = false; + public boolean whitelist = false; + } + + final class OnDemandDialogHolder { + public View dialog = null; + public CountDownLatch latch = new CountDownLatch(1); + public boolean reset = false; + } + + // Helper methods + + private OnDemandResult onDemandDialog(final Hook hook, final PRestriction restriction, final PRestriction result) { + final OnDemandResult oResult = new OnDemandResult(); + try { + int userId = Util.getUserId(restriction.uid); + + // Check if application + if (!PrivacyManager.isApplication(restriction.uid)) + if (!getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, false)) + return oResult; + + // Check for exceptions + if (hook != null && !hook.canOnDemand()) + return oResult; + if (!PrivacyManager.canRestrict(restriction.uid, getXUid(), restriction.restrictionName, + restriction.methodName, false)) + return oResult; + + // Check if enabled + if (!getSettingBool(userId, PrivacyManager.cSettingOnDemand, true)) + return oResult; + if (!getSettingBool(restriction.uid, PrivacyManager.cSettingOnDemand, false)) + return oResult; + + // Check version + String version = getSetting(new PSetting(userId, "", PrivacyManager.cSettingVersion, "0.0")).value; + if (new Version(version).compareTo(new Version("2.1.5")) < 0) + return oResult; + + // Get activity manager context + final Context context = getContext(); + if (context == null) + return oResult; + + long token = 0; + try { + token = Binder.clearCallingIdentity(); + + // Get application info + final ApplicationInfoEx appInfo = new ApplicationInfoEx(context, restriction.uid); + + // Check for system application + if (appInfo.isSystem()) + if (new Version(version).compareTo(new Version("2.0.38")) < 0) + return oResult; + + // Check if activity manager agrees + if (!XActivityManagerService.canOnDemand()) + return oResult; + + // Check if activity manager locked + if (isAMLocked(restriction.uid)) { + Util.log(null, Log.WARN, "On demand locked " + restriction); + return oResult; + } + + // Go ask + Util.log(null, Log.WARN, "On demand " + restriction); + mOndemandSemaphore.acquireUninterruptibly(); + try { + // Check if activity manager still agrees + if (!XActivityManagerService.canOnDemand()) + return oResult; + + // Check if activity manager locked now + if (isAMLocked(restriction.uid)) { + Util.log(null, Log.WARN, "On demand acquired locked " + restriction); + return oResult; + } + + Util.log(null, Log.WARN, "On demanding " + restriction); + + // Check if method not asked before + CRestriction mkey = new CRestriction(restriction, null); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(mkey)) { + CRestriction mrestriction = mRestrictionCache.get(mkey); + if (mrestriction.asked) { + Util.log(null, Log.WARN, "Already asked " + restriction); + result.restricted = mrestriction.restricted; + result.asked = true; + return oResult; + } + } + } + + // Check if category not asked before (once) + CRestriction ckey = new CRestriction(restriction, null); + ckey.setMethodName(null); + synchronized (mAskedOnceCache) { + if (mAskedOnceCache.containsKey(ckey)) { + CRestriction carestriction = mAskedOnceCache.get(ckey); + if (!carestriction.isExpired()) { + Util.log(null, Log.WARN, "Already asked once category " + restriction); + result.restricted = carestriction.restricted; + result.asked = true; + return oResult; + } + } + } + + // Check if method not asked before once + synchronized (mAskedOnceCache) { + if (mAskedOnceCache.containsKey(mkey)) { + CRestriction marestriction = mAskedOnceCache.get(mkey); + if (!marestriction.isExpired()) { + Util.log(null, Log.WARN, "Already asked once method " + restriction); + result.restricted = marestriction.restricted; + result.asked = true; + return oResult; + } + } + } + + // Check if whitelist not asked before + if (restriction.extra != null && hook != null && hook.whitelist() != null) { + CSetting skey = new CSetting(restriction.uid, hook.whitelist(), restriction.extra); + synchronized (mSettingCache) { + if (mSettingCache.containsKey(skey)) { + String value = mSettingCache.get(skey).getValue(); + if (value != null) { + Util.log(null, Log.WARN, "Already asked whitelist " + skey); + result.restricted = Boolean.parseBoolean(value); + result.asked = true; + return oResult; + } + } + for (String xextra : getXExtra(restriction, hook)) { + CSetting xkey = new CSetting(restriction.uid, hook.whitelist(), xextra); + if (mSettingCache.containsKey(xkey)) { + String value = mSettingCache.get(xkey).getValue(); + if (value != null) { + Util.log(null, Log.WARN, "Already asked whitelist " + xkey); + result.restricted = Boolean.parseBoolean(value); + result.asked = true; + return oResult; + } + } + } + } + } + + final OnDemandDialogHolder holder = new OnDemandDialogHolder(); + + // Build dialog parameters + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.type = WindowManager.LayoutParams.TYPE_PHONE; + params.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND; + params.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE; + params.dimAmount = 0.85f; + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + params.format = PixelFormat.TRANSLUCENT; + params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN; + params.gravity = Gravity.CENTER; + + // Get window manager + final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + + // Show dialog + mHandler.post(new Runnable() { + @Override + public void run() { + try { + // Build dialog + holder.dialog = getOnDemandView(restriction, hook, appInfo, result, context, holder, + oResult); + + // Handle reset button + ((Button) holder.dialog.findViewById(R.id.btnReset)) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ((ProgressBar) holder.dialog.findViewById(R.id.pbProgress)) + .setProgress(cMaxOnDemandDialog * 20); + holder.reset = true; + holder.latch.countDown(); + } + }); + + // Make dialog visible + wm.addView(holder.dialog, params); + + // Update progress bar + Runnable runProgress = new Runnable() { + @Override + public void run() { + if (holder.dialog != null && holder.dialog.isShown()) { + // Update progress bar + ProgressBar progressBar = (ProgressBar) holder.dialog + .findViewById(R.id.pbProgress); + if (progressBar.getProgress() > 0) { + progressBar.incrementProgressBy(-1); + mHandler.postDelayed(this, 50); + } + + // Check if activity manager locked + if (isAMLocked(restriction.uid)) { + Util.log(null, Log.WARN, "On demand dialog locked " + restriction); + ((Button) holder.dialog.findViewById(R.id.btnDontKnow)).callOnClick(); + } + } + } + }; + mHandler.postDelayed(runProgress, 50); + + // Enabled buttons after one second + boolean repeat = (SystemClock.elapsedRealtime() - mOnDemandLastAnswer < 1000); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (holder.dialog != null && holder.dialog.isShown()) { + holder.dialog.findViewById(R.id.btnAllow).setEnabled(true); + holder.dialog.findViewById(R.id.btnDontKnow).setEnabled(true); + holder.dialog.findViewById(R.id.btnDeny).setEnabled(true); + } + } + }, repeat ? 0 : 1000); + + } catch (NameNotFoundException ex) { + Util.log(null, Log.WARN, ex.toString()); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + + // Wait for choice, reset or timeout + do { + holder.reset = false; + boolean choice = holder.latch.await(cMaxOnDemandDialog, TimeUnit.SECONDS); + if (holder.reset) { + holder.latch = new CountDownLatch(1); + Util.log(null, Log.WARN, "On demand reset " + restriction); + } else if (choice) + oResult.ondemand = true; + else + Util.log(null, Log.WARN, "On demand timeout " + restriction); + } while (holder.reset); + mOnDemandLastAnswer = SystemClock.elapsedRealtime(); + + // Dismiss dialog + mHandler.post(new Runnable() { + @Override + public void run() { + View dialog = holder.dialog; + if (dialog != null) + wm.removeView(dialog); + } + }); + + } finally { + mOndemandSemaphore.release(); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + + return oResult; + } + + @SuppressLint("InflateParams") + private View getOnDemandView(final PRestriction restriction, final Hook hook, ApplicationInfoEx appInfo, + final PRestriction result, Context context, final OnDemandDialogHolder holder, final OnDemandResult oResult) + throws NameNotFoundException, RemoteException { + // Get resources + String self = PrivacyService.class.getPackage().getName(); + Resources resources = context.getPackageManager().getResourcesForApplication(self); + + // Reference views + final View view = LayoutInflater.from(context.createPackageContext(self, 0)).inflate(R.layout.ondemand, null); + ImageView ivAppIcon = (ImageView) view.findViewById(R.id.ivAppIcon); + TextView tvUid = (TextView) view.findViewById(R.id.tvUid); + TextView tvAppName = (TextView) view.findViewById(R.id.tvAppName); + TextView tvCategory = (TextView) view.findViewById(R.id.tvCategory); + TextView tvFunction = (TextView) view.findViewById(R.id.tvFunction); + TextView tvParameters = (TextView) view.findViewById(R.id.tvParameters); + TableRow rowParameters = (TableRow) view.findViewById(R.id.rowParameters); + TextView tvDefault = (TextView) view.findViewById(R.id.tvDefault); + TextView tvInfoCategory = (TextView) view.findViewById(R.id.tvInfoCategory); + final CheckBox cbExpert = (CheckBox) view.findViewById(R.id.cbExpert); + final CheckBox cbCategory = (CheckBox) view.findViewById(R.id.cbCategory); + final CheckBox cbOnce = (CheckBox) view.findViewById(R.id.cbOnce); + final Spinner spOnce = (Spinner) view.findViewById(R.id.spOnce); + final LinearLayout llWhiteList = (LinearLayout) view.findViewById(R.id.llWhiteList); + final CheckBox cbWhitelist = (CheckBox) view.findViewById(R.id.cbWhitelist); + final CheckBox cbWhitelistExtra1 = (CheckBox) view.findViewById(R.id.cbWhitelistExtra1); + final CheckBox cbWhitelistExtra2 = (CheckBox) view.findViewById(R.id.cbWhitelistExtra2); + final CheckBox cbWhitelistExtra3 = (CheckBox) view.findViewById(R.id.cbWhitelistExtra3); + ProgressBar mProgress = (ProgressBar) view.findViewById(R.id.pbProgress); + Button btnDeny = (Button) view.findViewById(R.id.btnDeny); + Button btnDontKnow = (Button) view.findViewById(R.id.btnDontKnow); + Button btnAllow = (Button) view.findViewById(R.id.btnAllow); + + final int userId = Util.getUserId(Process.myUid()); + boolean expert = getSettingBool(userId, PrivacyManager.cSettingODExpert, false); + boolean category = getSettingBool(userId, PrivacyManager.cSettingODCategory, true); + boolean once = getSettingBool(userId, PrivacyManager.cSettingODOnce, false); + expert = expert || !category || once; + final boolean whitelistDangerous = (hook != null && hook.isDangerous() && hook.whitelist() != null); + + // Set values + if ((hook != null && hook.isDangerous()) || appInfo.isSystem()) + view.setBackgroundResource(R.color.color_dangerous_dialog); + else + view.setBackgroundResource(android.R.color.background_dark); + + // Application information + ivAppIcon.setImageDrawable(appInfo.getIcon(context)); + tvUid.setText(Integer.toString(appInfo.getUid())); + tvAppName.setText(TextUtils.join(", ", appInfo.getApplicationName())); + + // Restriction information + int catId = resources.getIdentifier("restrict_" + restriction.restrictionName, "string", self); + tvCategory.setText(resources.getString(catId)); + tvFunction.setText(restriction.methodName); + if (restriction.extra == null) + rowParameters.setVisibility(View.GONE); + else + tvParameters.setText(restriction.extra); + String defaultAction = resources.getString(result.restricted ? R.string.title_deny : R.string.title_allow); + tvDefault.setText(defaultAction); + + // Help + int helpId = resources.getIdentifier("restrict_help_" + restriction.restrictionName, "string", self); + tvInfoCategory.setText(resources.getString(helpId)); + + // Expert mode + cbExpert.setChecked(expert); + if (expert) { + for (View child : Util.getViewsByTag((ViewGroup) view, "details")) + child.setVisibility(View.VISIBLE); + for (View child : Util.getViewsByTag((ViewGroup) view, "nodetails")) + child.setVisibility(View.GONE); + } + if (expert || whitelistDangerous) + llWhiteList.setVisibility(View.VISIBLE); + + // Category + cbCategory.setChecked(category); + + // Once + cbOnce.setChecked(once); + int osel = Integer + .parseInt(getSetting(new PSetting(userId, "", PrivacyManager.cSettingODOnceDuration, "0")).value); + spOnce.setSelection(osel); + + // Whitelisting + if (hook != null && hook.whitelist() != null && restriction.extra != null) { + cbWhitelist.setText(resources.getString(R.string.title_whitelist, restriction.extra)); + cbWhitelist.setVisibility(View.VISIBLE); + String[] xextra = getXExtra(restriction, hook); + if (xextra.length > 0) { + cbWhitelistExtra1.setText(resources.getString(R.string.title_whitelist, xextra[0])); + cbWhitelistExtra1.setVisibility(View.VISIBLE); + } + if (xextra.length > 1) { + cbWhitelistExtra2.setText(resources.getString(R.string.title_whitelist, xextra[1])); + cbWhitelistExtra2.setVisibility(View.VISIBLE); + } + if (xextra.length > 2) { + cbWhitelistExtra3.setText(resources.getString(R.string.title_whitelist, xextra[2])); + cbWhitelistExtra3.setVisibility(View.VISIBLE); + } + } + + cbExpert.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + setSettingBool(userId, "", PrivacyManager.cSettingODExpert, isChecked); + if (!isChecked) { + setSettingBool(userId, "", PrivacyManager.cSettingODCategory, true); + setSettingBool(userId, "", PrivacyManager.cSettingODOnce, false); + cbCategory.setChecked(true); + cbOnce.setChecked(false); + cbWhitelist.setChecked(false); + cbWhitelistExtra1.setChecked(false); + cbWhitelistExtra2.setChecked(false); + cbWhitelistExtra3.setChecked(false); + } + + for (View child : Util.getViewsByTag((ViewGroup) view, "details")) + child.setVisibility(isChecked ? View.VISIBLE : View.GONE); + for (View child : Util.getViewsByTag((ViewGroup) view, "nodetails")) + child.setVisibility(isChecked ? View.GONE : View.VISIBLE); + + if (!whitelistDangerous) + llWhiteList.setVisibility(isChecked ? View.VISIBLE : View.GONE); + } + }); + + // Category, once and whitelist exclude each other + cbCategory.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cbWhitelist.setChecked(false); + cbWhitelistExtra1.setChecked(false); + cbWhitelistExtra2.setChecked(false); + cbWhitelistExtra3.setChecked(false); + } + } + }); + cbOnce.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cbWhitelist.setChecked(false); + cbWhitelistExtra1.setChecked(false); + cbWhitelistExtra2.setChecked(false); + cbWhitelistExtra3.setChecked(false); + } + } + }); + cbWhitelist.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cbCategory.setChecked(false); + cbOnce.setChecked(false); + cbWhitelistExtra1.setChecked(false); + cbWhitelistExtra2.setChecked(false); + cbWhitelistExtra3.setChecked(false); + } + } + }); + cbWhitelistExtra1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cbCategory.setChecked(false); + cbOnce.setChecked(false); + cbWhitelist.setChecked(false); + cbWhitelistExtra2.setChecked(false); + cbWhitelistExtra3.setChecked(false); + } + } + }); + cbWhitelistExtra2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cbCategory.setChecked(false); + cbOnce.setChecked(false); + cbWhitelist.setChecked(false); + cbWhitelistExtra1.setChecked(false); + cbWhitelistExtra3.setChecked(false); + } + } + }); + cbWhitelistExtra3.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cbCategory.setChecked(false); + cbOnce.setChecked(false); + cbWhitelist.setChecked(false); + cbWhitelistExtra1.setChecked(false); + cbWhitelistExtra2.setChecked(false); + } + } + }); + + // Setup progress bar + mProgress.setMax(cMaxOnDemandDialog * 20); + mProgress.setProgress(cMaxOnDemandDialog * 20); + + btnAllow.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Allow + result.restricted = false; + result.asked = true; + + if (cbWhitelist.isChecked()) + onDemandWhitelist(restriction, null, result, oResult, hook); + else if (cbWhitelistExtra1.isChecked()) + onDemandWhitelist(restriction, getXExtra(restriction, hook)[0], result, oResult, hook); + else if (cbWhitelistExtra2.isChecked()) + onDemandWhitelist(restriction, getXExtra(restriction, hook)[1], result, oResult, hook); + else if (cbWhitelistExtra3.isChecked()) + onDemandWhitelist(restriction, getXExtra(restriction, hook)[2], result, oResult, hook); + else { + setSettingBool(userId, "", PrivacyManager.cSettingODCategory, cbCategory.isChecked()); + setSettingBool(userId, "", PrivacyManager.cSettingODOnce, cbOnce.isChecked()); + + if (cbOnce.isChecked()) + onDemandOnce(restriction, cbCategory.isChecked(), result, oResult, spOnce); + else + onDemandChoice(restriction, cbCategory.isChecked(), false); + } + holder.latch.countDown(); + } + }); + + btnDontKnow.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Deny once + result.restricted = !(hook != null && hook.isDangerous()); + result.asked = true; + onDemandOnce(restriction, false, result, oResult, spOnce); + holder.latch.countDown(); + } + }); + + btnDeny.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Deny + result.restricted = true; + result.asked = true; + + if (cbWhitelist.isChecked()) + onDemandWhitelist(restriction, null, result, oResult, hook); + else if (cbWhitelistExtra1.isChecked()) + onDemandWhitelist(restriction, getXExtra(restriction, hook)[0], result, oResult, hook); + else if (cbWhitelistExtra2.isChecked()) + onDemandWhitelist(restriction, getXExtra(restriction, hook)[1], result, oResult, hook); + else if (cbWhitelistExtra3.isChecked()) + onDemandWhitelist(restriction, getXExtra(restriction, hook)[2], result, oResult, hook); + else { + setSettingBool(userId, "", PrivacyManager.cSettingODCategory, cbCategory.isChecked()); + setSettingBool(userId, "", PrivacyManager.cSettingODOnce, cbOnce.isChecked()); + + if (cbOnce.isChecked()) + onDemandOnce(restriction, cbCategory.isChecked(), result, oResult, spOnce); + else + onDemandChoice(restriction, cbCategory.isChecked(), true); + } + holder.latch.countDown(); + } + }); + + return view; + } + + private String[] getXExtra(PRestriction restriction, Hook hook) { + List listResult = new ArrayList(); + if (restriction.extra != null && hook != null) + if (hook.whitelist().equals(Meta.cTypeFilename)) { + // Top folders of file name + File file = new File(restriction.extra); + for (int i = 1; i <= 3 && file != null; i++) { + String parent = file.getParent(); + if (!TextUtils.isEmpty(parent)) + listResult.add(parent + File.separatorChar + "*"); + file = file.getParentFile(); + } + + } else if (hook.whitelist().equals(Meta.cTypeIPAddress)) { + // sub-domain or sub-net + int colon = restriction.extra.lastIndexOf(':'); + String address = (colon >= 0 ? restriction.extra.substring(0, colon) : restriction.extra); + if (Patterns.IP_ADDRESS.matcher(address).matches()) { + int dot = address.lastIndexOf('.'); + listResult.add(address.substring(0, dot) + ".*" + + (colon >= 0 ? restriction.extra.substring(colon) : "")); + if (colon >= 0) + listResult.add(address.substring(0, dot) + ".*:*"); + } else { + int dot = restriction.extra.indexOf('.'); + if (dot > 0) { + listResult.add('*' + restriction.extra.substring(dot)); + if (colon >= 0) + listResult.add('*' + restriction.extra.substring(dot, colon) + ":*"); + } + } + + } else if (hook.whitelist().equals(Meta.cTypeUrl)) { + // Top folders of file name + Uri uri = Uri.parse(restriction.extra); + if ("file".equals(uri.getScheme())) { + File file = new File(uri.getPath()); + for (int i = 1; i <= 3 && file != null; i++) { + String parent = file.getParent(); + if (!TextUtils.isEmpty(parent)) + listResult.add(parent + File.separatorChar + "*"); + file = file.getParentFile(); + } + } + + } else if (hook.whitelist().equals(Meta.cTypeMethod) || hook.whitelist().equals(Meta.cTypeTransaction) + || hook.whitelist().equals(Meta.cTypeAction)) { + String[] component = restriction.extra.split(":"); + if (component.length == 2) + listResult.add(component[0] + ":*"); + } + + return listResult.toArray(new String[0]); + } + + private void onDemandWhitelist(PRestriction restriction, String xextra, PRestriction result, + OnDemandResult oResult, Hook hook) { + try { + Util.log(null, Log.WARN, (result.restricted ? "Black" : "White") + "listing " + restriction + " xextra=" + + xextra); + + oResult.whitelist = true; + + // Set white/black list + setSettingInternal(new PSetting(restriction.uid, hook.whitelist(), (xextra == null ? restriction.extra + : xextra), Boolean.toString(!result.restricted))); + + if (!PrivacyManager.getSettingBool(restriction.uid, PrivacyManager.cSettingWhitelistNoModify, false)) + if (restriction.methodName == null || !PrivacyManager.cMethodNoState.contains(restriction.methodName)) { + // Mark state as changed + setSettingInternal(new PSetting(restriction.uid, "", PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_CHANGED))); + + // Update modification time + setSettingInternal(new PSetting(restriction.uid, "", PrivacyManager.cSettingModifyTime, + Long.toString(System.currentTimeMillis()))); + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + private void onDemandOnce(PRestriction restriction, boolean category, PRestriction result, OnDemandResult oResult, + Spinner spOnce) { + oResult.once = true; + + // Get duration + String value = (String) spOnce.getSelectedItem(); + if (value == null) + result.time = new Date().getTime() + PrivacyManager.cRestrictionCacheTimeoutMs; + else { + char unit = value.charAt(value.length() - 1); + value = value.substring(0, value.length() - 1); + if (unit == 's') + result.time = new Date().getTime() + Integer.parseInt(value) * 1000; + else if (unit == 'm') + result.time = new Date().getTime() + Integer.parseInt(value) * 60 * 1000; + else + result.time = new Date().getTime() + PrivacyManager.cRestrictionCacheTimeoutMs; + + try { + int userId = Util.getUserId(restriction.uid); + String sel = Integer.toString(spOnce.getSelectedItemPosition()); + setSettingInternal(new PSetting(userId, "", PrivacyManager.cSettingODOnceDuration, sel)); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + Util.log(null, Log.WARN, (result.restricted ? "Deny" : "Allow") + " once " + restriction + " category=" + + category + " until=" + new Date(result.time)); + + CRestriction key = new CRestriction(result, null); + key.setExpiry(result.time); + if (category) { + key.setMethodName(null); + key.setExtra(null); + } + synchronized (mAskedOnceCache) { + if (mAskedOnceCache.containsKey(key)) + mAskedOnceCache.remove(key); + mAskedOnceCache.put(key, key); + } + } + + private void onDemandChoice(PRestriction restriction, boolean category, boolean restrict) { + try { + PRestriction result = new PRestriction(restriction); + + // Get current category restriction state + boolean prevRestricted = false; + CRestriction key = new CRestriction(restriction.uid, restriction.restrictionName, null, null); + synchronized (mRestrictionCache) { + if (mRestrictionCache.containsKey(key)) + prevRestricted = mRestrictionCache.get(key).restricted; + } + + Util.log(null, Log.WARN, "On demand choice " + restriction + " category=" + category + " restrict=" + + restrict + " prev=" + prevRestricted); + + if (category || (restrict && restrict != prevRestricted)) { + // Set category restriction + result.methodName = null; + result.restricted = restrict; + result.asked = category; + setRestrictionInternal(result); + + // Clear category on change + for (Hook hook : PrivacyManager.getHooks(restriction.restrictionName, null)) + if (!PrivacyManager.canRestrict(restriction.uid, getXUid(), restriction.restrictionName, + hook.getName(), false)) { + result.methodName = hook.getName(); + result.restricted = false; + result.asked = true; + setRestrictionInternal(result); + } else { + result.methodName = hook.getName(); + result.restricted = restrict && !hook.isDangerous(); + result.asked = category || (hook.isDangerous() && hook.whitelist() == null); + setRestrictionInternal(result); + } + } + + if (!category) { + // Set method restriction + result.methodName = restriction.methodName; + result.restricted = restrict; + result.asked = true; + result.extra = restriction.extra; + setRestrictionInternal(result); + } + + // Mark state as changed + setSettingInternal(new PSetting(restriction.uid, "", PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_CHANGED))); + + // Update modification time + setSettingInternal(new PSetting(restriction.uid, "", PrivacyManager.cSettingModifyTime, + Long.toString(System.currentTimeMillis()))); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + private void notifyRestricted(final PRestriction restriction) { + final Context context = getContext(); + if (context != null && mHandler != null) + mHandler.post(new Runnable() { + @Override + public void run() { + long token = 0; + try { + token = Binder.clearCallingIdentity(); + + // Get resources + String self = PrivacyService.class.getPackage().getName(); + Resources resources = context.getPackageManager().getResourcesForApplication(self); + + // Notify user + String text = resources.getString(R.string.msg_restrictedby); + text += " (" + restriction.uid + " " + restriction.restrictionName + "/" + + restriction.methodName + ")"; + Toast.makeText(context, text, Toast.LENGTH_LONG).show(); + + } catch (NameNotFoundException ex) { + Util.bug(null, ex); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + private void notifyException(Throwable ex) { + Util.bug(null, ex); + + if (mNotified) + return; + + Context context = getContext(); + if (context == null) + return; + + try { + Intent intent = new Intent("biz.bokhorst.xprivacy.action.EXCEPTION"); + intent.putExtra("Message", ex.toString()); + context.sendBroadcast(intent); + + NotificationManager notificationManager = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + + // Build notification + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context); + notificationBuilder.setSmallIcon(R.drawable.ic_launcher); + notificationBuilder.setContentTitle(context.getString(R.string.app_name)); + notificationBuilder.setContentText(ex.toString()); + notificationBuilder.setWhen(System.currentTimeMillis()); + notificationBuilder.setAutoCancel(true); + Notification notification = notificationBuilder.build(); + + // Display notification + notificationManager.notify(Util.NOTIFY_CORRUPT, notification); + + mNotified = true; + } catch (Throwable exex) { + Util.bug(null, exex); + } + } + + private boolean getSettingBool(int uid, String name, boolean defaultValue) throws RemoteException { + return getSettingBool(uid, "", name, defaultValue); + } + + private boolean getSettingBool(int uid, String type, String name, boolean defaultValue) throws RemoteException { + String value = getSetting(new PSetting(uid, type, name, Boolean.toString(defaultValue))).value; + return Boolean.parseBoolean(value); + } + + private void setSettingBool(int uid, String type, String name, boolean value) { + try { + setSettingInternal(new PSetting(uid, type, name, Boolean.toString(value))); + } catch (RemoteException ex) { + Util.bug(null, ex); + } + } + + private void enforcePermission(int uid) { + if (uid >= 0) + if (Util.getUserId(uid) != Util.getUserId(Binder.getCallingUid())) + throw new SecurityException("uid=" + uid + " calling=" + Binder.getCallingUid()); + + int callingUid = Util.getAppId(Binder.getCallingUid()); + if (callingUid != getXUid() && callingUid != Process.SYSTEM_UID) + throw new SecurityException("xuid=" + mXUid + " calling=" + Binder.getCallingUid()); + } + + private boolean isAMLocked(int uid) { + try { + Object am; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + am = mAm; + else { + Class cam = Class.forName("com.android.server.am.ActivityManagerService"); + am = cam.getMethod("self").invoke(null); + } + boolean locked = Thread.holdsLock(am); + if (locked) { + boolean freeze = getSettingBool(uid, PrivacyManager.cSettingFreeze, false); + if (!freeze) + freeze = getSettingBool(0, PrivacyManager.cSettingFreeze, false); + if (freeze) + return false; + } + return locked; + } catch (Throwable ex) { + Util.bug(null, ex); + return false; + } + } + + private Context getContext() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + return mContext; + else { + // public static ActivityManagerService self() + // frameworks/base/services/java/com/android/server/am/ActivityManagerService.java + try { + Class cam = Class.forName("com.android.server.am.ActivityManagerService"); + Object am = cam.getMethod("self").invoke(null); + if (am == null) + return null; + Field mContext = cam.getDeclaredField("mContext"); + mContext.setAccessible(true); + return (Context) mContext.get(am); + } catch (Throwable ex) { + Util.bug(null, ex); + return null; + } + } + } + + private int getIsolatedUid(int uid) { + if (PrivacyManager.isIsolated(uid)) + try { + Field fmIsolatedProcesses = null; + Class cam = mAm.getClass(); + while (cam != null && fmIsolatedProcesses == null) + try { + fmIsolatedProcesses = cam.getDeclaredField("mIsolatedProcesses"); + } catch (NoSuchFieldException ignored) { + cam = cam.getSuperclass(); + } + + if (fmIsolatedProcesses == null) + throw new Exception(mAm.getClass().getName() + ".mIsolatedProcesses not found"); + + fmIsolatedProcesses.setAccessible(true); + SparseArray mIsolatedProcesses = (SparseArray) fmIsolatedProcesses.get(mAm); + Object processRecord = mIsolatedProcesses.get(uid); + Field fInfo = processRecord.getClass().getDeclaredField("info"); + fInfo.setAccessible(true); + ApplicationInfo info = (ApplicationInfo) fInfo.get(processRecord); + + Util.log(null, Log.WARN, "Translated isolated uid=" + uid + " into application uid=" + info.uid + + " pkg=" + info.packageName); + return info.uid; + } catch (Throwable ex) { + Util.bug(null, ex); + } + return uid; + } + + private int getXUid() { + if (mXUid < 0) + try { + Context context = getContext(); + if (context != null) { + PackageManager pm = context.getPackageManager(); + if (pm != null) { + String self = PrivacyService.class.getPackage().getName(); + ApplicationInfo xInfo = pm.getApplicationInfo(self, 0); + mXUid = xInfo.uid; + } + } + } catch (Throwable ignored) { + // The package manager may not be up-to-date yet + } + return mXUid; + } + + private File getDbFile() { + return new File(Environment.getDataDirectory() + File.separator + "system" + File.separator + "xprivacy" + + File.separator + "xprivacy.db"); + } + + private File getDbUsageFile() { + return new File(Environment.getDataDirectory() + File.separator + "system" + File.separator + "xprivacy" + + File.separator + "usage.db"); + } + + private void setupDatabase() { + try { + File dbFile = getDbFile(); + + // Create database folder + dbFile.getParentFile().mkdirs(); + + // Check database folder + if (dbFile.getParentFile().isDirectory()) + Util.log(null, Log.WARN, "Database folder=" + dbFile.getParentFile()); + else + Util.log(null, Log.WARN, "Does not exist folder=" + dbFile.getParentFile()); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // Move database from data/xprivacy folder + File folder = new File(Environment.getDataDirectory() + File.separator + "xprivacy"); + if (folder.exists()) { + File[] oldFiles = folder.listFiles(); + if (oldFiles != null) + for (File file : oldFiles) + if (file.getName().startsWith("xprivacy.db") || file.getName().startsWith("usage.db")) { + File target = new File(dbFile.getParentFile() + File.separator + file.getName()); + boolean status = Util.move(file, target); + Util.log(null, Log.WARN, "Moved " + file + " to " + target + " ok=" + status); + } + Util.log(null, Log.WARN, "Deleting folder=" + folder); + folder.delete(); + } + + // Move database from data/application folder + folder = new File(Environment.getDataDirectory() + File.separator + "data" + File.separator + + PrivacyService.class.getPackage().getName()); + if (folder.exists()) { + File[] oldFiles = folder.listFiles(); + if (oldFiles != null) + for (File file : oldFiles) + if (file.getName().startsWith("xprivacy.db")) { + File target = new File(dbFile.getParentFile() + File.separator + file.getName()); + boolean status = Util.move(file, target); + Util.log(null, Log.WARN, "Moved " + file + " to " + target + " ok=" + status); + } + Util.log(null, Log.WARN, "Deleting folder=" + folder); + folder.delete(); + } + } + + // Set database file permissions + // Owner: rwx (system) + // Group: rwx (system) + // World: --- + Util.setPermissions(dbFile.getParentFile().getAbsolutePath(), 0770, Process.SYSTEM_UID, Process.SYSTEM_UID); + File[] files = dbFile.getParentFile().listFiles(); + if (files != null) + for (File file : files) + if (file.getName().startsWith("xprivacy.db") || file.getName().startsWith("usage.db")) + Util.setPermissions(file.getAbsolutePath(), 0770, Process.SYSTEM_UID, Process.SYSTEM_UID); + + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + private SQLiteDatabase getDb() { + synchronized (this) { + // Check current reference + if (mDb != null && !mDb.isOpen()) { + mDb = null; + Util.log(null, Log.ERROR, "Database not open"); + } + + if (mDb == null) + try { + setupDatabase(); + + // Create/upgrade database when needed + File dbFile = getDbFile(); + SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, null); + + // Check database integrity + if (db.isDatabaseIntegrityOk()) + Util.log(null, Log.WARN, "Database integrity ok"); + else { + // http://www.sqlite.org/howtocorrupt.html + mCorrupt = true; + Util.log(null, Log.ERROR, "Database corrupt"); + Cursor cursor = db.rawQuery("PRAGMA integrity_check", null); + try { + while (cursor.moveToNext()) { + String message = cursor.getString(0); + Util.log(null, Log.ERROR, message); + } + } finally { + cursor.close(); + } + db.close(); + + // Backup database file + File dbBackup = new File(dbFile.getParentFile() + File.separator + "xprivacy.backup"); + dbBackup.delete(); + dbFile.renameTo(dbBackup); + + File dbJournal = new File(dbFile.getAbsolutePath() + "-journal"); + File dbJournalBackup = new File(dbBackup.getAbsolutePath() + "-journal"); + dbJournalBackup.delete(); + dbJournal.renameTo(dbJournalBackup); + + Util.log(null, Log.ERROR, "Old database backup: " + dbBackup.getAbsolutePath()); + + // Create new database + db = SQLiteDatabase.openOrCreateDatabase(dbFile, null); + Util.log(null, Log.ERROR, "New, empty database created"); + } + + // Update migration status + if (db.getVersion() > 1) { + Util.log(null, Log.WARN, "Updating migration status"); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + ContentValues values = new ContentValues(); + values.put("uid", 0); + if (db.getVersion() > 9) + values.put("type", ""); + values.put("name", PrivacyManager.cSettingMigrated); + values.put("value", Boolean.toString(true)); + db.insertWithOnConflict(cTableSetting, null, values, SQLiteDatabase.CONFLICT_REPLACE); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + // Upgrade database if needed + if (db.needUpgrade(1)) { + Util.log(null, Log.WARN, "Creating database"); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + // http://www.sqlite.org/lang_createtable.html + db.execSQL("CREATE TABLE restriction (uid INTEGER NOT NULL, restriction TEXT NOT NULL, method TEXT NOT NULL, restricted INTEGER NOT NULL)"); + db.execSQL("CREATE TABLE setting (uid INTEGER NOT NULL, name TEXT NOT NULL, value TEXT)"); + db.execSQL("CREATE TABLE usage (uid INTEGER NOT NULL, restriction TEXT NOT NULL, method TEXT NOT NULL, restricted INTEGER NOT NULL, time INTEGER NOT NULL)"); + db.execSQL("CREATE UNIQUE INDEX idx_restriction ON restriction(uid, restriction, method)"); + db.execSQL("CREATE UNIQUE INDEX idx_setting ON setting(uid, name)"); + db.execSQL("CREATE UNIQUE INDEX idx_usage ON usage(uid, restriction, method)"); + db.setVersion(1); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + + } + + if (db.needUpgrade(2)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + // Old migrated indication + db.setVersion(2); + } + + if (db.needUpgrade(3)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DELETE FROM usage WHERE method=''"); + db.setVersion(3); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(4)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DELETE FROM setting WHERE value IS NULL"); + db.setVersion(4); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(5)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DELETE FROM setting WHERE value = ''"); + db.execSQL("DELETE FROM setting WHERE name = 'Random@boot' AND value = 'false'"); + db.setVersion(5); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(6)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DELETE FROM setting WHERE name LIKE 'OnDemand.%'"); + db.setVersion(6); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(7)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("ALTER TABLE usage ADD COLUMN extra TEXT"); + db.setVersion(7); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(8)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DROP INDEX idx_usage"); + db.execSQL("CREATE UNIQUE INDEX idx_usage ON usage(uid, restriction, method, extra)"); + db.setVersion(8); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(9)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("DROP TABLE usage"); + db.setVersion(9); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(10)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + db.execSQL("ALTER TABLE setting ADD COLUMN type TEXT"); + db.execSQL("DROP INDEX idx_setting"); + db.execSQL("CREATE UNIQUE INDEX idx_setting ON setting(uid, type, name)"); + db.execSQL("UPDATE setting SET type=''"); + db.setVersion(10); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + if (db.needUpgrade(11)) { + Util.log(null, Log.WARN, "Upgrading from version=" + db.getVersion()); + mLock.writeLock().lock(); + try { + db.beginTransaction(); + try { + List listSetting = new ArrayList(); + Cursor cursor = db.query(cTableSetting, new String[] { "uid", "name", "value" }, null, + null, null, null, null); + if (cursor != null) + try { + while (cursor.moveToNext()) { + int uid = cursor.getInt(0); + String name = cursor.getString(1); + String value = cursor.getString(2); + if (name.startsWith("Account.") || name.startsWith("Application.") + || name.startsWith("Contact.") || name.startsWith("Template.")) { + int dot = name.indexOf('.'); + String type = name.substring(0, dot); + listSetting + .add(new PSetting(uid, type, name.substring(dot + 1), value)); + listSetting.add(new PSetting(uid, "", name, null)); + + } else if (name.startsWith("Whitelist.")) { + String[] component = name.split("\\."); + listSetting.add(new PSetting(uid, component[1], name.replace( + component[0] + "." + component[1] + ".", ""), value)); + listSetting.add(new PSetting(uid, "", name, null)); + } + } + } finally { + cursor.close(); + } + + for (PSetting setting : listSetting) { + Util.log(null, Log.WARN, "Converting " + setting); + if (setting.value == null) + db.delete(cTableSetting, "uid=? AND type=? AND name=?", + new String[] { Integer.toString(setting.uid), setting.type, + setting.name }); + else { + // Create record + ContentValues values = new ContentValues(); + values.put("uid", setting.uid); + values.put("type", setting.type); + values.put("name", setting.name); + values.put("value", setting.value); + + // Insert/update record + db.insertWithOnConflict(cTableSetting, null, values, + SQLiteDatabase.CONFLICT_REPLACE); + } + } + + db.setVersion(11); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } finally { + mLock.writeLock().unlock(); + } + } + + Util.log(null, Log.WARN, "Running VACUUM"); + mLock.writeLock().lock(); + try { + db.execSQL("VACUUM"); + } catch (Throwable ex) { + Util.bug(null, ex); + } finally { + mLock.writeLock().unlock(); + } + + Util.log(null, Log.WARN, "Database version=" + db.getVersion()); + mDb = db; + } catch (Throwable ex) { + mDb = null; // retry + Util.bug(null, ex); + try { + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream( + "/cache/xprivacy.log", true)); + outputStreamWriter.write(ex.toString()); + outputStreamWriter.write("\n"); + outputStreamWriter.write(Log.getStackTraceString(ex)); + outputStreamWriter.write("\n"); + outputStreamWriter.close(); + } catch (Throwable exex) { + Util.bug(null, exex); + } + } + + return mDb; + } + } + + private SQLiteDatabase getDbUsage() { + synchronized (this) { + // Check current reference + if (mDbUsage != null && !mDbUsage.isOpen()) { + mDbUsage = null; + Util.log(null, Log.ERROR, "Usage database not open"); + } + + if (mDbUsage == null) + try { + // Create/upgrade database when needed + File dbUsageFile = getDbUsageFile(); + SQLiteDatabase dbUsage = SQLiteDatabase.openOrCreateDatabase(dbUsageFile, null); + + // Check database integrity + if (dbUsage.isDatabaseIntegrityOk()) + Util.log(null, Log.WARN, "Usage database integrity ok"); + else { + dbUsage.close(); + dbUsageFile.delete(); + new File(dbUsageFile + "-journal").delete(); + dbUsage = SQLiteDatabase.openOrCreateDatabase(dbUsageFile, null); + Util.log(null, Log.WARN, "Deleted corrupt usage data database"); + } + + // Upgrade database if needed + if (dbUsage.needUpgrade(1)) { + Util.log(null, Log.WARN, "Creating usage database"); + mLockUsage.writeLock().lock(); + try { + dbUsage.beginTransaction(); + try { + dbUsage.execSQL("CREATE TABLE usage (uid INTEGER NOT NULL, restriction TEXT NOT NULL, method TEXT NOT NULL, extra TEXT NOT NULL, restricted INTEGER NOT NULL, time INTEGER NOT NULL)"); + dbUsage.execSQL("CREATE UNIQUE INDEX idx_usage ON usage(uid, restriction, method, extra)"); + dbUsage.setVersion(1); + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.writeLock().unlock(); + } + } + + if (dbUsage.needUpgrade(2)) { + Util.log(null, Log.WARN, "Upgrading usage database from version=" + dbUsage.getVersion()); + mLockUsage.writeLock().lock(); + try { + dbUsage.beginTransaction(); + try { + dbUsage.execSQL("ALTER TABLE usage ADD COLUMN value TEXT"); + dbUsage.setVersion(2); + dbUsage.setTransactionSuccessful(); + } finally { + dbUsage.endTransaction(); + } + } finally { + mLockUsage.writeLock().unlock(); + } + } + + Util.log(null, Log.WARN, "Changing to asynchronous mode"); + try { + dbUsage.rawQuery("PRAGMA synchronous=OFF", null); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + Util.log(null, Log.WARN, "Usage database version=" + dbUsage.getVersion()); + mDbUsage = dbUsage; + } catch (Throwable ex) { + mDbUsage = null; // retry + Util.bug(null, ex); + } + + return mDbUsage; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/RState.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/RState.java new file mode 100644 index 0000000..a3d8bec --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/RState.java @@ -0,0 +1,124 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Process; + +public class RState { + public int mUid; + public String mRestrictionName; + public String mMethodName; + public boolean restricted; + public boolean asked = false; + public boolean partialRestricted = false; + public boolean partialAsk = false; + + public RState(int uid, String restrictionName, String methodName, Version version) { + mUid = uid; + mRestrictionName = restrictionName; + mMethodName = methodName; + + int userId = Util.getUserId(Process.myUid()); + + // Get if on demand + boolean onDemand = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemand, true); + if (onDemand) + onDemand = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingOnDemand, false); + + boolean allRestricted = true; + boolean someRestricted = false; + boolean allAsk = true; + boolean someAsk = false; + + if (methodName == null) { + if (restrictionName == null) { + // Examine the category state + someAsk = onDemand; + for (String rRestrictionName : PrivacyManager.getRestrictions()) { + PRestriction query = PrivacyManager.getRestrictionEx(uid, rRestrictionName, null); + allRestricted = (allRestricted && query.restricted); + someRestricted = (someRestricted || query.restricted); + allAsk = (allAsk && !query.asked); + someAsk = (someAsk || !query.asked); + } + asked = !onDemand; + } else { + // Examine the category/method states + PRestriction query = PrivacyManager.getRestrictionEx(uid, restrictionName, null); + someRestricted = query.restricted; + someAsk = !query.asked; + for (PRestriction restriction : PrivacyManager.getRestrictionList(uid, restrictionName)) { + Hook hook = PrivacyManager.getHook(restrictionName, restriction.methodName); + if (version != null && hook != null && hook.getFrom() != null + && version.compareTo(hook.getFrom()) < 0) + continue; + + allRestricted = (allRestricted && restriction.restricted); + someRestricted = (someRestricted || restriction.restricted); + if (hook == null || hook.canOnDemand()) { + allAsk = (allAsk && !restriction.asked); + someAsk = (someAsk || !restriction.asked); + } + } + asked = query.asked; + } + } else { + // Examine the method state + PRestriction query = PrivacyManager.getRestrictionEx(uid, restrictionName, methodName); + allRestricted = query.restricted; + someRestricted = false; + asked = query.asked; + } + + boolean isApp = PrivacyManager.isApplication(uid); + boolean odSystem = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingOnDemandSystem, false); + + restricted = (allRestricted || someRestricted); + asked = (!onDemand || !(isApp || odSystem) || asked); + partialRestricted = (!allRestricted && someRestricted); + partialAsk = (onDemand && (isApp || odSystem) && !allAsk && someAsk); + } + + public void toggleRestriction() { + if (mMethodName == null) { + // Get restrictions to change + List listRestriction; + if (mRestrictionName == null) + listRestriction = PrivacyManager.getRestrictions(); + else { + listRestriction = new ArrayList(); + listRestriction.add(mRestrictionName); + } + + // Change restriction + if (restricted) + PrivacyManager.deleteRestrictions(mUid, mRestrictionName, (mRestrictionName == null)); + else { + for (String restrictionName : listRestriction) + PrivacyManager.setRestriction(mUid, restrictionName, null, true, false); + PrivacyManager.updateState(mUid); + } + } else { + PRestriction query = PrivacyManager.getRestrictionEx(mUid, mRestrictionName, null); + PrivacyManager.setRestriction(mUid, mRestrictionName, mMethodName, !restricted, query.asked); + PrivacyManager.updateState(mUid); + } + } + + public void toggleAsked() { + asked = !asked; + if (mRestrictionName == null) + PrivacyManager.setSetting(mUid, PrivacyManager.cSettingOnDemand, Boolean.toString(!asked)); + else { + // Avoid re-doing all exceptions for dangerous functions + List listPRestriction = new ArrayList(); + listPRestriction.add(new PRestriction(mUid, mRestrictionName, mMethodName, restricted, asked)); + PrivacyManager.setRestrictionList(listPRestriction); + PrivacyManager.setSetting(mUid, PrivacyManager.cSettingState, + Integer.toString(ApplicationInfoEx.STATE_CHANGED)); + PrivacyManager.setSetting(mUid, PrivacyManager.cSettingModifyTime, + Long.toString(System.currentTimeMillis())); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Requirements.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Requirements.java new file mode 100644 index 0000000..0e97c1e --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Requirements.java @@ -0,0 +1,442 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.Inet4Address; +import java.net.InterfaceAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.location.GpsStatus; +import android.net.Uri; +import android.net.wifi.WifiInfo; +import android.os.Build; +import android.os.IBinder; +import android.text.TextUtils; +import android.util.Log; + +public class Requirements { + private static String[] cIncompatible = new String[] { "com.lbe.security" }; + + @SuppressWarnings("unchecked") + public static void check(final ActivityBase context) { + // Check Android version + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(R.string.app_wrongandroid); + alertDialogBuilder.setIcon(context.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent androidIntent = new Intent(Intent.ACTION_VIEW); + androidIntent.setData(Uri.parse("https://github.com/M66B/XPrivacy#installation")); + context.startActivity(androidIntent); + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + // Check if XPrivacy is enabled + if (Util.isXposedEnabled()) { + // Check privacy client + try { + if (PrivacyService.checkClient()) { + List listError = (List) PrivacyService.getClient().check(); + if (listError.size() > 0) + sendSupportInfo(TextUtils.join("\r\n", listError), context); + } + } catch (Throwable ex) { + sendSupportInfo(ex.toString(), context); + } + } else { + // @formatter:off + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(R.string.app_notenabled); + alertDialogBuilder.setIcon(context.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent xInstallerIntent = new Intent("de.robv.android.xposed.installer.OPEN_SECTION") + .setPackage("de.robv.android.xposed.installer") + .putExtra("section", "modules") + .putExtra("module", context.getPackageName()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(xInstallerIntent); + } + }); + // @formatter:on + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + // Check pro enabler + Version version = Util.getProEnablerVersion(context); + if (version != null && !Util.isValidProEnablerVersion(version)) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(R.string.app_wrongenabler); + alertDialogBuilder.setIcon(context.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent storeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + + context.getPackageName() + ".pro")); + context.startActivity(storeIntent); + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + // Check incompatible apps + checkCompatibility(context); + + // Check activity thread + try { + Class clazz = Class.forName("android.app.ActivityThread", false, null); + try { + clazz.getDeclaredMethod("unscheduleGcIdler"); + } catch (NoSuchMethodException ex) { + reportClass(clazz, context); + } + } catch (ClassNotFoundException ex) { + sendSupportInfo(ex.toString(), context); + } + + // Check activity thread receiver data + try { + Class clazz = Class.forName("android.app.ActivityThread$ReceiverData", false, null); + if (!checkField(clazz, "intent")) + reportClass(clazz, context); + } catch (ClassNotFoundException ex) { + try { + reportClass(Class.forName("android.app.ActivityThread", false, null), context); + } catch (ClassNotFoundException exex) { + sendSupportInfo(exex.toString(), context); + } + } + + // Check file utils + try { + Class clazz = Class.forName("android.os.FileUtils", false, null); + try { + clazz.getDeclaredMethod("setPermissions", String.class, int.class, int.class, int.class); + } catch (NoSuchMethodException ex) { + reportClass(clazz, context); + } + } catch (ClassNotFoundException ex) { + sendSupportInfo(ex.toString(), context); + } + + // Check interface address + if (!checkField(InterfaceAddress.class, "address") || !checkField(InterfaceAddress.class, "broadcastAddress") + || (PrivacyService.getClient() != null && PrivacyManager.getDefacedProp(0, "InetAddress") == null)) + reportClass(InterfaceAddress.class, context); + + // Check package manager service + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + try { + Class clazz = Class.forName("com.android.server.pm.PackageManagerService", false, null); + try { + try { + clazz.getDeclaredMethod("getPackageUid", String.class, int.class); + } catch (NoSuchMethodException ignored) { + clazz.getDeclaredMethod("getPackageUid", String.class); + } + } catch (NoSuchMethodException ex) { + reportClass(clazz, context); + } + } catch (ClassNotFoundException ex) { + sendSupportInfo(ex.toString(), context); + } + + // Check GPS status + if (!checkField(GpsStatus.class, "mSatellites")) + reportClass(GpsStatus.class, context); + + // Check service manager + try { + Class clazz = Class.forName("android.os.ServiceManager", false, null); + try { + // @formatter:off + // public static void addService(String name, IBinder service) + // public static void addService(String name, IBinder service, boolean allowIsolated) + // public static String[] listServices() + // public static IBinder checkService(String name) + // @formatter:on + + Method listServices = clazz.getDeclaredMethod("listServices"); + Method getService = clazz.getDeclaredMethod("getService", String.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) + clazz.getDeclaredMethod("addService", String.class, IBinder.class, boolean.class); + else + clazz.getDeclaredMethod("addService", String.class, IBinder.class); + + // Get services + Map mapService = new HashMap(); + String[] services = (String[]) listServices.invoke(null); + if (services != null) + for (String service : services) + if (service != null) { + IBinder binder = (IBinder) getService.invoke(null, service); + String descriptor = (binder == null ? null : binder.getInterfaceDescriptor()); + mapService.put(service, descriptor); + } + + if (mapService.size() > 0) { + // Check services + int i = 0; + List listMissing = new ArrayList(); + for (String name : XBinder.cServiceName) { + String descriptor = XBinder.cServiceDescriptor.get(i++); + if (descriptor != null && !XBinder.cServiceOptional.contains(name)) { + // Check name + boolean checkDescriptor = false; + + if (name.equals("telephony.registry")) { + if (mapService.containsKey(name)) + checkDescriptor = true; + else if (!mapService.containsKey("telephony.msim.registry")) + listMissing.add(name); + + } else if (name.equals("telephony.msim.registry")) { + if (mapService.containsKey(name)) + checkDescriptor = true; + else if (!mapService.containsKey("telephony.registry")) + listMissing.add(name); + + } else if (name.equals("bluetooth")) { + if (mapService.containsKey(name)) + checkDescriptor = true; + else if (!mapService.containsKey("bluetooth_manager")) + listMissing.add(name); + + } else if (name.equals("bluetooth_manager")) { + if (mapService.containsKey(name)) + checkDescriptor = true; + else if (!mapService.containsKey("bluetooth")) + listMissing.add(name); + + } else { + if (mapService.containsKey(name)) + checkDescriptor = true; + else + listMissing.add(name); + } + + // Check descriptor + if (checkDescriptor) { + String d = mapService.get(name); + if (d != null && !d.equals(descriptor)) + listMissing.add(descriptor); + } + } + } + + // Check result + if (listMissing.size() > 0) { + List listService = new ArrayList(); + for (String service : mapService.keySet()) + listService.add(String.format("%s: %s", service, mapService.get(service))); + sendSupportInfo("Missing:\r\n" + TextUtils.join("\r\n", listMissing) + "\r\n\r\nAvailable:\r\n" + + TextUtils.join("\r\n", listService), context); + } + } + } catch (NoSuchMethodException ex) { + reportClass(clazz, context); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } catch (ClassNotFoundException ex) { + sendSupportInfo(ex.toString(), context); + } + + // Check wifi info + if (!checkField(WifiInfo.class, "mSupplicantState") || !checkField(WifiInfo.class, "mBSSID") + || !checkField(WifiInfo.class, "mIpAddress") || !checkField(WifiInfo.class, "mMacAddress") + || !(checkField(WifiInfo.class, "mSSID") || checkField(WifiInfo.class, "mWifiSsid"))) + reportClass(WifiInfo.class, context); + + // Check mWifiSsid.octets + if (checkField(WifiInfo.class, "mWifiSsid")) + try { + Class clazz = Class.forName("android.net.wifi.WifiSsid", false, null); + try { + clazz.getDeclaredMethod("createFromAsciiEncoded", String.class); + } catch (NoSuchMethodException ex) { + reportClass(clazz, context); + } + } catch (ClassNotFoundException ex) { + sendSupportInfo(ex.toString(), context); + } + + // Check Inet4Address/ANY + try { + Inet4Address.class.getDeclaredField("ANY"); + } catch (Throwable ex) { + reportClass(Inet4Address.class, context); + } + + // Check context services + checkService(context, Context.ACCOUNT_SERVICE, + new String[] { "android.accounts.AccountManager", "com.intel.arkham.ExtendAccountManager" /* Asus */, + "android.privacy.surrogate.PrivacyAccountManager" /* PDroid */}); + checkService(context, Context.ACTIVITY_SERVICE, new String[] { "android.app.ActivityManager", + "android.app.ActivityManagerEx" }); + checkService(context, Context.CLIPBOARD_SERVICE, new String[] { "android.content.ClipboardManager" }); + checkService(context, Context.CONNECTIVITY_SERVICE, new String[] { "android.net.ConnectivityManager", + "android.net.ConnectivityManagerEx", "android.net.MultiSimConnectivityManager", + "android.privacy.surrogate.PrivacyConnectivityManager" /* PDroid */}); + checkService(context, Context.LOCATION_SERVICE, + new String[] { "android.location.LocationManager", "android.location.ZTEPrivacyLocationManager", + "android.privacy.surrogate.PrivacyLocationManager" /* PDroid */}); + Class serviceClass = context.getPackageManager().getClass(); + if (!"android.app.ApplicationPackageManager".equals(serviceClass.getName()) + && !"amazon.content.pm.AmazonPackageManagerImpl".equals(serviceClass.getName())) + reportClass(serviceClass, context); + checkService(context, Context.SENSOR_SERVICE, new String[] { "android.hardware.SensorManager", + "android.hardware.SystemSensorManager" }); + checkService(context, Context.TELEPHONY_SERVICE, new String[] { "android.telephony.TelephonyManager", + "android.telephony.MSimTelephonyManager", "android.telephony.MultiSimTelephonyManager", + "android.telephony.ZTEPrivacyTelephonyManager", "android.telephony.ZTEPrivacyMSimTelephonyManager", + "com.motorola.android.telephony.MotoTelephonyManager", + "android.privacy.surrogate.PrivacyTelephonyManager" /* PDroid */}); + checkService(context, Context.WINDOW_SERVICE, new String[] { "android.view.WindowManagerImpl", + "android.view.Window$LocalWindowManager", "amazon.view.AmazonWindowManagerImpl" }); + checkService(context, Context.WIFI_SERVICE, new String[] { "android.net.wifi.WifiManager", + "com.amazon.net.AmazonWifiManager", "com.amazon.android.service.AmazonWifiManager", + "android.privacy.surrogate.PrivacyWifiManager" /* PDroid */}); + } + + public static void checkService(ActivityBase context, String name, String[] className) { + Object service = context.getSystemService(name); + if (service == null) + sendSupportInfo("Service missing name=" + name, context); + else if (!Arrays.asList(className).contains(service.getClass().getName())) + reportClass(service.getClass(), context); + } + + public static void checkCompatibility(ActivityBase context) { + for (String packageName : cIncompatible) + try { + ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(packageName, 0); + if (appInfo.enabled) { + String name = context.getPackageManager().getApplicationLabel(appInfo).toString(); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(String.format(context.getString(R.string.app_incompatible), name)); + alertDialogBuilder.setIcon(context.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + } catch (NameNotFoundException ex) { + } + } + + private static boolean checkField(Class clazz, String fieldName) { + try { + clazz.getDeclaredField(fieldName); + return true; + } catch (NoSuchFieldException ex) { + return false; + } + } + + private static void reportClass(Class clazz, ActivityBase context) { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("Incompatible %s", clazz.getName())); + sb.append("\r\n"); + sb.append("\r\n"); + for (Constructor constructor : clazz.getConstructors()) { + sb.append(constructor.toString()); + sb.append("\r\n"); + } + sb.append("\r\n"); + for (Method method : clazz.getDeclaredMethods()) { + sb.append(method.toString()); + sb.append("\r\n"); + } + sb.append("\r\n"); + for (Field field : clazz.getDeclaredFields()) { + sb.append(field.toString()); + sb.append("\r\n"); + } + sb.append("\r\n"); + sendSupportInfo(sb.toString(), context); + } + + public static void sendSupportInfo(final String text, final ActivityBase context) { + Util.log(null, Log.WARN, text); + + if (Util.hasValidFingerPrint(context) && !"Genymotion".equals(Build.MANUFACTURER)) { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context); + alertDialogBuilder.setTitle(R.string.app_name); + alertDialogBuilder.setMessage(R.string.msg_support_info); + alertDialogBuilder.setIcon(context.getThemed(R.attr.icon_launcher)); + alertDialogBuilder.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int _which) { + String ourVersion = Util.getSelfVersionName(context); + StringBuilder sb = new StringBuilder(text); + sb.insert(0, "\r\n"); + sb.insert(0, String.format("Id: %s\r\n", Build.ID)); + sb.insert(0, String.format("Display: %s\r\n", Build.DISPLAY)); + sb.insert(0, String.format("Host: %s\r\n", Build.HOST)); + sb.insert(0, String.format("Device: %s\r\n", Build.DEVICE)); + sb.insert(0, String.format("Product: %s\r\n", Build.PRODUCT)); + sb.insert(0, String.format("Model: %s\r\n", Build.MODEL)); + sb.insert(0, String.format("Manufacturer: %s\r\n", Build.MANUFACTURER)); + sb.insert(0, String.format("Brand: %s\r\n", Build.BRAND)); + sb.insert(0, "\r\n"); + sb.insert(0, String.format("Android: %s (SDK %d)\r\n", Build.VERSION.RELEASE, + Build.VERSION.SDK_INT)); + sb.insert(0, String.format("XPrivacy: %s\r\n", ourVersion)); + + Intent sendEmail = new Intent(Intent.ACTION_SEND); + sendEmail.setType("message/rfc822"); + sendEmail.putExtra(Intent.EXTRA_EMAIL, new String[] { "marcel+support@faircode.eu" }); + sendEmail.putExtra(Intent.EXTRA_SUBJECT, "XPrivacy " + ourVersion + "/" + + Build.VERSION.RELEASE + " support info"); + sendEmail.putExtra(Intent.EXTRA_TEXT, sb.toString()); + try { + context.startActivity(sendEmail); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + alertDialogBuilder.setNegativeButton(context.getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/SharedPreferencesEx.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/SharedPreferencesEx.java new file mode 100644 index 0000000..0bfe683 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/SharedPreferencesEx.java @@ -0,0 +1,242 @@ +package biz.bokhorst.xprivacy; + +// Based on: +// https://github.com/rovo89/XposedBridge/blob/master/src/de/robv/android/xposed/XSharedPreferences.java + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import android.content.SharedPreferences; +import android.os.Process; +import android.util.Log; + +import com.android.internal.util.XmlUtils; + +/** + * This class is basically the same as SharedPreferencesImpl from AOSP, but + * read-only and without listeners support. Instead, it is made to be compatible + * with all ROMs. + */ +public final class SharedPreferencesEx implements SharedPreferences { + private final File mFile; + private final File mBackupFile; + private Map mMap; + private boolean mLoaded = false; + private long mLastModified; + private long mFileSize; + + private static int cTryMaxCount = 10; + private static int cTryWaitMs = 50; + + public SharedPreferencesEx(File prefFile) { + mFile = prefFile; + mBackupFile = makeBackupFile(prefFile); + startLoadFromDisk(); + } + + public SharedPreferencesEx(String packageName, String prefFileName) { + mFile = new File(Util.getUserDataDirectory(Process.myUid()) + File.pathSeparator + "shared_prefs" + + File.pathSeparator + prefFileName + ".xml"); + mBackupFile = makeBackupFile(mFile); + startLoadFromDisk(); + } + + private void startLoadFromDisk() { + synchronized (this) { + mLoaded = false; + } + new Thread("SharedPreferencesEx-load") { + @Override + public void run() { + synchronized (SharedPreferencesEx.this) { + loadFromDiskLocked(); + } + } + }.start(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void loadFromDiskLocked() { + int tries = 0; + while (++tries <= cTryMaxCount && !mLoaded && (mFile.exists() || mBackupFile.exists())) { + // Log retry + if (tries > 1) + Util.log(null, Log.WARN, "Load " + mFile + " try=" + tries + " exists=" + mFile.exists() + " readable=" + + mFile.canRead() + " backup=" + mBackupFile.exists()); + + // Read file if possible + if (mFile.exists() && mFile.canRead() && !mBackupFile.exists()) { + Map map = null; + long lastModified = mFile.lastModified(); + long fileSize = mFile.length(); + BufferedInputStream str = null; + try { + str = new BufferedInputStream(new FileInputStream(mFile), 16 * 1024); + map = XmlUtils.readMapXml(str); + } catch (Throwable ex) { + Util.log(null, Log.WARN, "Error reading " + mFile + ": " + ex); + } finally { + if (str != null) { + try { + str.close(); + } catch (RuntimeException rethrown) { + throw rethrown; + } catch (Throwable ex) { + Util.log(null, Log.WARN, "Error closing " + mFile + ": " + ex); + } + } + } + if (map != null) { + mLoaded = true; + mMap = map; + mLastModified = lastModified; + mFileSize = fileSize; + notifyAll(); + } + } + + // Wait for next try + if (!mLoaded && tries < cTryMaxCount) + try { + Thread.sleep(cTryWaitMs); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + // File not read + if (!mLoaded) { + if (tries >= cTryMaxCount) + // Not loaded: try to load again on next access + Util.log(null, Log.ERROR, "Not loaded " + mFile); + else + mLoaded = true; + mMap = new HashMap(); + notifyAll(); + } + } + + private static File makeBackupFile(File prefsFile) { + return new File(prefsFile.getPath() + ".bak"); + } + + /** + * Reload the settings from file if they have changed. + */ + public void reload() { + synchronized (this) { + if (hasFileChanged()) + startLoadFromDisk(); + } + } + + private boolean hasFileChanged() { + // canRead returns false for non existing files + if (!mFile.canRead() || mBackupFile.exists()) + return true; + + long lastModified = mFile.lastModified(); + long fileSize = mFile.length(); + synchronized (this) { + return (mLastModified != lastModified || mFileSize != fileSize); + } + } + + private void awaitLoadedLocked() { + while (!mLoaded) + try { + wait(); + } catch (InterruptedException unused) { + } + } + + @Override + public Map getAll() { + synchronized (this) { + awaitLoadedLocked(); + return new HashMap(mMap); + } + } + + @Override + public String getString(String key, String defValue) { + synchronized (this) { + awaitLoadedLocked(); + String v = (String) mMap.get(key); + return v != null ? v : defValue; + } + } + + @Override + @SuppressWarnings("unchecked") + public Set getStringSet(String key, Set defValues) { + synchronized (this) { + awaitLoadedLocked(); + Set v = (Set) mMap.get(key); + return v != null ? v : defValues; + } + } + + @Override + public int getInt(String key, int defValue) { + synchronized (this) { + awaitLoadedLocked(); + Integer v = (Integer) mMap.get(key); + return v != null ? v : defValue; + } + } + + @Override + public long getLong(String key, long defValue) { + synchronized (this) { + awaitLoadedLocked(); + Long v = (Long) mMap.get(key); + return v != null ? v : defValue; + } + } + + @Override + public float getFloat(String key, float defValue) { + synchronized (this) { + awaitLoadedLocked(); + Float v = (Float) mMap.get(key); + return v != null ? v : defValue; + } + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + synchronized (this) { + awaitLoadedLocked(); + Boolean v = (Boolean) mMap.get(key); + return v != null ? v : defValue; + } + } + + @Override + public boolean contains(String key) { + synchronized (this) { + awaitLoadedLocked(); + return mMap.containsKey(key); + } + } + + @Override + public Editor edit() { + throw new UnsupportedOperationException("read-only implementation"); + } + + @Override + public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { + throw new UnsupportedOperationException("listeners are not supported in this implementation"); + } + + @Override + public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { + throw new UnsupportedOperationException("listeners are not supported in this implementation"); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/UpdateService.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/UpdateService.java new file mode 100644 index 0000000..be87a30 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/UpdateService.java @@ -0,0 +1,388 @@ +package biz.bokhorst.xprivacy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +public class UpdateService extends Service { + public static final String cAction = "Action"; + public static final int cActionBoot = 1; + public static final int cActionUpdated = 2; + public static final String cFlush = "biz.bokhorst.xprivacy.action.FLUSH"; + public static final String cUpdate = "biz.bokhorst.xprivacy.action.UPDATE"; + + private static Thread mWorkerThread; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // Check if work + if (intent == null) { + stopSelf(); + return 0; + } + + // Flush + if (cFlush.equals(intent.getAction())) { + try { + PrivacyService.getClient().flush(); + XApplication.manage(this, 0, XApplication.cActionFlush); + } catch (Throwable ex) { + Util.bug(null, ex); + } + stopSelf(); + return 0; + } + + // Update + if (cUpdate.equals(intent.getAction())) { + if (Util.hasProLicense(this) != null) { + int userId = Util.getUserId(Process.myUid()); + boolean updates = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingUpdates, false); + if (updates) + new ActivityShare.UpdateTask(this).execute(); + } + stopSelf(); + return 0; + } + + // Check action + Bundle extras = intent.getExtras(); + if (extras.containsKey(cAction)) { + final int action = extras.getInt(cAction); + Util.log(null, Log.WARN, "Service received action=" + action + " flags=" + flags); + + // Check service + if (PrivacyService.getClient() == null) { + Util.log(null, Log.ERROR, "Service not available"); + stopSelf(); + return 0; + } + + // Start foreground service + NotificationCompat.Builder builder = new NotificationCompat.Builder(UpdateService.this); + builder.setSmallIcon(R.drawable.ic_launcher); + builder.setContentTitle(getString(R.string.app_name)); + builder.setContentText(getString(R.string.msg_service)); + builder.setWhen(System.currentTimeMillis()); + builder.setAutoCancel(false); + builder.setOngoing(true); + Notification notification = builder.build(); + startForeground(Util.NOTIFY_SERVICE, notification); + + // Start worker + mWorkerThread = new Thread(new Runnable() { + @Override + public void run() { + try { + // Check action + if (action == cActionBoot) { + // Boot received + migrate(UpdateService.this); + upgrade(UpdateService.this); + randomize(UpdateService.this); + + } else if (action == cActionUpdated) { + // Self updated + upgrade(UpdateService.this); + + } else + Util.log(null, Log.ERROR, "Unknown action=" + action); + + // Done + stopForeground(true); + stopSelf(); + } catch (Throwable ex) { + Util.bug(null, ex); + // Leave service running + } + } + + }); + mWorkerThread.start(); + } else + Util.log(null, Log.ERROR, "Action missing"); + + return START_STICKY; + } + + private static void migrate(Context context) throws IOException, RemoteException { + int first = 0; + String format = context.getString(R.string.msg_migrating); + List listApp = context.getPackageManager().getInstalledApplications(0); + + // Start migrate + PrivacyProvider.migrateLegacy(context); + + // Migrate global settings + PrivacyManager.setSettingList(PrivacyProvider.migrateSettings(context, 0)); + PrivacyProvider.finishMigrateSettings(0); + + // Migrate application settings/restrictions + for (int i = 1; i <= listApp.size(); i++) { + int uid = listApp.get(i - 1).uid; + // Settings + List listSetting = PrivacyProvider.migrateSettings(context, uid); + PrivacyManager.setSettingList(listSetting); + PrivacyProvider.finishMigrateSettings(uid); + + // Restrictions + List listRestriction = PrivacyProvider.migrateRestrictions(context, uid); + PrivacyManager.setRestrictionList(listRestriction); + PrivacyProvider.finishMigrateRestrictions(uid); + + if (first == 0) + if (listSetting.size() > 0 || listRestriction.size() > 0) + first = i; + if (first > 0 && first < listApp.size()) + notifyProgress(context, Util.NOTIFY_MIGRATE, format, 100 * (i - first) / (listApp.size() - first)); + } + if (first == 0) + Util.log(null, Log.WARN, "Nothing to migrate"); + + // Complete migration + int userId = Util.getUserId(Process.myUid()); + PrivacyService.getClient().setSetting( + new PSetting(userId, "", PrivacyManager.cSettingMigrated, Boolean.toString(true))); + } + + private static void upgrade(Context context) throws NameNotFoundException { + // Get previous version number + int userId = Util.getUserId(Process.myUid()); + Version currentVersion = new Version(Util.getSelfVersionName(context)); + Version storedVersion = new Version(PrivacyManager.getSetting(userId, PrivacyManager.cSettingVersion, "0.0")); + boolean dangerous = PrivacyManager.getSettingBool(userId, PrivacyManager.cSettingDangerous, false); + + // Check if upgrade needed + if (storedVersion.compareTo(new Version("0.0")) != 0 && currentVersion.compareTo(storedVersion) > 0) { + Util.log(null, Log.WARN, "Starting upgrade from version " + storedVersion + " to version " + currentVersion + + " dangerous=" + dangerous); + + // Upgrade packages + int first = 0; + String format = context.getString(R.string.msg_upgrading); + List listApp = context.getPackageManager().getInstalledApplications(0); + + for (int i = 1; i <= listApp.size(); i++) { + int uid = listApp.get(i - 1).uid; + List listRestriction = getUpgradeWork(storedVersion, uid, dangerous); + PrivacyManager.setRestrictionList(listRestriction); + + // Reset on demand for system applications + if (new ApplicationInfoEx(context, listApp.get(i - 1).uid).isSystem()) + if (storedVersion.compareTo(new Version("2.0.38")) < 0) + if (PrivacyManager.getSettingBool(listApp.get(i - 1).uid, PrivacyManager.cSettingOnDemand, + false)) { + Util.log(null, Log.WARN, "Disabling on demand for uid=" + listApp.get(i - 1).uid); + PrivacyManager.setSetting(listApp.get(i - 1).uid, PrivacyManager.cSettingOnDemand, null); + } + + if (first == 0) + if (listRestriction.size() > 0) + first = i; + if (first > 0 && first < listApp.size()) + notifyProgress(context, Util.NOTIFY_UPGRADE, format, 100 * (i - first) / (listApp.size() - first)); + } + if (first == 0) + Util.log(null, Log.WARN, "Nothing to upgrade from version " + storedVersion + " to " + currentVersion); + + // Remove legacy setting + if (dangerous) + PrivacyManager.setSetting(userId, PrivacyManager.cSettingDangerous, null); + + // Resolve quirk + if (storedVersion.compareTo(new Version("2.99.28")) < 0) + if (!PrivacyManager.getSettingBool(0, PrivacyManager.cSettingNoResolve, false)) { + Util.log(null, Log.WARN, "Enabling quirk resolve"); + PrivacyManager.setSetting(0, PrivacyManager.cSettingResolve, Boolean.toString(true)); + } + + // Wipe template + if (storedVersion.compareTo(new Version("2.0.34")) < 0) + for (PSetting setting : PrivacyManager.getSettingList(0, null)) + if (Meta.cTypeTemplate.equals(setting.type)) { + Util.log(null, Log.WARN, "Deleting " + setting); + PrivacyManager.setSetting(setting.uid, setting.type, setting.name, null); + } + } else + Util.log(null, Log.WARN, "No upgrade from version " + storedVersion + " to " + currentVersion); + + // Set new version number + if (currentVersion.compareTo(storedVersion) > 0) + PrivacyManager.setSetting(userId, PrivacyManager.cSettingVersion, currentVersion.toString()); + + // Cleanup + PrivacyManager.removeLegacySalt(userId); + } + + private static void randomize(Context context) { + int first = 0; + String format = context.getString(R.string.msg_randomizing); + List listApp = context.getPackageManager().getInstalledApplications(0); + + // Randomize global + int userId = Util.getUserId(Process.myUid()); + PrivacyManager.setSettingList(getRandomizeWork(context, userId)); + + // Randomize applications + for (int i = 1; i <= listApp.size(); i++) { + int uid = listApp.get(i - 1).uid; + List listSetting = getRandomizeWork(context, uid); + PrivacyManager.setSettingList(listSetting); + + if (first == 0) + if (listSetting.size() > 0) + first = i; + if (first > 0 && first < listApp.size()) + notifyProgress(context, Util.NOTIFY_RANDOMIZE, format, 100 * (i - first) / (listApp.size() - first)); + } + if (first == 0) + Util.log(null, Log.WARN, "Nothing to randomize"); + } + + private static List getRandomizeWork(Context context, int uid) { + List listWork = new ArrayList(); + + if (PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingRandom, false)) { + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingLatitude)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingLatitude, PrivacyManager.getRandomProp("LAT"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingLongitude)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingLongitude, PrivacyManager + .getRandomProp("LON"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingAltitude)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingAltitude, PrivacyManager.getRandomProp("ALT"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingSerial)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingSerial, PrivacyManager + .getRandomProp("SERIAL"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingMac)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingMac, PrivacyManager.getRandomProp("MAC"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingPhone)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingPhone, PrivacyManager.getRandomProp("PHONE"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingImei)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingImei, PrivacyManager.getRandomProp("IMEI"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingId)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingId, PrivacyManager + .getRandomProp("ANDROID_ID"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingGsfId)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingGsfId, PrivacyManager.getRandomProp("GSF_ID"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingAdId)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingAdId, PrivacyManager + .getRandomProp("AdvertisingId"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingCountry)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingCountry, PrivacyManager + .getRandomProp("ISO3166"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingSubscriber)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingSubscriber, PrivacyManager + .getRandomProp("SubscriberId"))); + + if (!hasRandomOnAccess(uid, PrivacyManager.cSettingSSID)) + listWork.add(new PSetting(uid, "", PrivacyManager.cSettingSSID, PrivacyManager.getRandomProp("SSID"))); + } + + return listWork; + } + + private static boolean hasRandomOnAccess(int uid, String setting) { + return PrivacyManager.cValueRandom.equals(PrivacyManager.getSetting(uid, setting, null)); + } + + private static List getUpgradeWork(Version sVersion, int uid, boolean dangerous) { + List listWork = new ArrayList(); + + for (String restrictionName : PrivacyManager.getRestrictions()) { + boolean restricted = PrivacyManager.getRestrictionEx(uid, restrictionName, null).restricted; + + for (Hook hook : PrivacyManager.getHooks(restrictionName, null)) { + // Disable new dangerous restrictions + if (hook.getFrom() != null) { + if (sVersion.compareTo(hook.getFrom()) < 0) { + if (hook.isDangerous()) { + Util.log(null, Log.WARN, "Upgrading dangerous " + hook + " from=" + hook.getFrom() + + " uid=" + uid); + PRestriction restriction = new PRestriction(uid, hook.getRestrictionName(), hook.getName(), + false, true); + listWork.add(restriction); + } + + // Restrict replaced methods + if (hook.getReplacedMethod() != null) { + if ("false".equals(hook.getReplacedMethod())) { + listWork.add(new PRestriction(uid, hook.getRestrictionName(), hook.getName(), false, + false)); + Util.log(null, Log.WARN, "Resetting restriction " + hook + " uid=" + uid); + } else { + PRestriction restriction = PrivacyManager.getRestrictionEx(uid, + hook.getReplacedRestriction(), hook.getReplacedMethod()); + listWork.add(new PRestriction(uid, hook.getRestrictionName(), hook.getName(), + restriction.restricted, restriction.asked)); + Util.log(null, Log.WARN, + "Replacing " + hook.getReplacedRestriction() + "/" + hook.getReplacedMethod() + + " by " + hook + " from=" + hook.getFrom() + " uid=" + uid); + } + } + } + } + + // Restrict dangerous + if (dangerous && restricted && hook.isDangerous()) { + PRestriction restriction = new PRestriction(uid, hook.getRestrictionName(), hook.getName(), true, + hook.whitelist() == null); + if (PrivacyManager.isRestrictionSet(restriction)) + Util.log(null, Log.WARN, "Restrict dangerous set restriction=" + restriction); + else { + Util.log(null, Log.WARN, "Restrict dangerous setting restriction=" + restriction); + listWork.add(restriction); + } + } + } + } + + return listWork; + } + + private static void notifyProgress(Context context, int id, String format, int percentage) { + String message = String.format(format, String.format("%d %%", percentage)); + Util.log(null, Log.WARN, message); + + NotificationManager notificationManager = (NotificationManager) context + .getSystemService(Context.NOTIFICATION_SERVICE); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + builder.setSmallIcon(R.drawable.ic_launcher); + builder.setContentTitle(context.getString(R.string.app_name)); + builder.setContentText(message); + builder.setWhen(System.currentTimeMillis()); + builder.setAutoCancel(percentage == 100); + builder.setOngoing(percentage < 100); + Notification notification = builder.build(); + notificationManager.notify(id, notification); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Util.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Util.java new file mode 100644 index 0000000..bfb3b0c --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Util.java @@ -0,0 +1,679 @@ +package biz.bokhorst.xprivacy; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.StackOverflowError; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.RuntimeException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.nio.channels.FileChannel; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; + +import javax.net.ssl.SSLPeerUnverifiedException; + +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.HttpHostConnectException; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.os.Process; +import android.os.RemoteException; +import android.os.TransactionTooLargeException; +import android.os.UserHandle; +import android.util.Base64; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +@SuppressWarnings("deprecation") +public class Util { + private static boolean mPro = false; + private static boolean mLog = true; + private static boolean mLogDetermined = false; + private static Boolean mHasLBE = null; + + private static Version MIN_PRO_VERSION = new Version("1.20"); + private static String LICENSE_FILE_NAME = "XPrivacy_license.txt"; + + public static int NOTIFY_RESTART = 0; + public static int NOTIFY_NOTXPOSED = 1; + public static int NOTIFY_SERVICE = 2; + public static int NOTIFY_MIGRATE = 3; + public static int NOTIFY_RANDOMIZE = 4; + public static int NOTIFY_UPGRADE = 5; + public static int NOTIFY_UPDATE = 6; + public static int NOTIFY_CORRUPT = 7; + + public static void log(XHook hook, int priority, String msg) { + // Check if logging enabled + int uid = Process.myUid(); + if (!mLogDetermined && uid > 0) { + mLogDetermined = true; + try { + mLog = PrivacyManager.getSettingBool(0, PrivacyManager.cSettingLog, false); + } catch (Throwable ignored) { + mLog = false; + } + } + + // Log if enabled + if (priority != Log.DEBUG && (priority == Log.INFO ? mLog : true)) + if (hook == null) + Log.println(priority, "XPrivacy", msg); + else + Log.println(priority, String.format("XPrivacy/%s", hook.getClass().getSimpleName()), msg); + + // Report to service + if (uid > 0 && priority == Log.ERROR) + if (PrivacyService.isRegistered()) + PrivacyService.reportErrorInternal(msg); + else + try { + IPrivacyService client = PrivacyService.getClient(); + if (client != null) + client.reportError(msg); + } catch (RemoteException ignored) { + } + } + + public static void bug(XHook hook, Throwable ex) { + if (ex instanceof InvocationTargetException) { + InvocationTargetException exex = (InvocationTargetException) ex; + if (exex.getTargetException() != null) + ex = exex.getTargetException(); + } + + int priority; + if (ex instanceof ActivityShare.AbortException) + priority = Log.WARN; + else if (ex instanceof ActivityShare.ServerException) + priority = Log.WARN; + else if (ex instanceof ConnectTimeoutException) + priority = Log.WARN; + else if (ex instanceof FileNotFoundException) + priority = Log.WARN; + else if (ex instanceof HttpHostConnectException) + priority = Log.WARN; + else if (ex instanceof NameNotFoundException) + priority = Log.WARN; + else if (ex instanceof NoClassDefFoundError) + priority = Log.WARN; + else if (ex instanceof OutOfMemoryError) + priority = Log.WARN; + else if (ex instanceof RuntimeException) + priority = Log.WARN; + else if (ex instanceof SecurityException) + priority = Log.WARN; + else if (ex instanceof SocketTimeoutException) + priority = Log.WARN; + else if (ex instanceof SSLPeerUnverifiedException) + priority = Log.WARN; + else if (ex instanceof StackOverflowError) + priority = Log.WARN; + else if (ex instanceof TransactionTooLargeException) + priority = Log.WARN; + else if (ex instanceof UnknownHostException) + priority = Log.WARN; + else if (ex instanceof UnsatisfiedLinkError) + priority = Log.WARN; + else + priority = Log.ERROR; + + boolean xprivacy = false; + for (StackTraceElement frame : ex.getStackTrace()) + if (frame.getClassName() != null && frame.getClassName().startsWith("biz.bokhorst.xprivacy")) { + xprivacy = true; + break; + } + if (!xprivacy) + priority = Log.WARN; + + log(hook, priority, ex.toString() + " uid=" + Process.myUid() + "\n" + Log.getStackTraceString(ex)); + } + + public static void logStack(XHook hook, int priority) { + logStack(hook, priority, false); + } + + public static void logStack(XHook hook, int priority, boolean cl) { + StringBuilder trace = new StringBuilder(); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { + trace.append(ste.toString()); + if (cl) + try { + Class clazz = Class.forName(ste.getClassName(), false, loader); + trace.append(" ["); + trace.append(clazz.getClassLoader().toString()); + trace.append("]"); + } catch (ClassNotFoundException ignored) { + } + trace.append("\n"); + } + log(hook, priority, trace.toString()); + } + + public static boolean isXposedEnabled() { + // Will be hooked to return true + log(null, Log.WARN, "XPrivacy not enabled"); + return false; + } + + public static void setPro(boolean enabled) { + mPro = enabled; + } + + public static boolean isProEnabled() { + return mPro; + } + + public static String hasProLicense(Context context) { + try { + // Get license + String[] license = getProLicenseUnchecked(); + if (license == null) + return null; + String name = license[0]; + String email = license[1]; + String signature = license[2]; + + // Get bytes + byte[] bEmail = email.getBytes("UTF-8"); + byte[] bSignature = hex2bytes(signature); + if (bEmail.length == 0 || bSignature.length == 0) { + Util.log(null, Log.ERROR, "Licensing: invalid file"); + return null; + } + + // Verify license + boolean licensed = verifyData(bEmail, bSignature, getPublicKey(context)); + if (licensed) + Util.log(null, Log.INFO, "Licensing: ok"); + else + Util.log(null, Log.ERROR, "Licensing: invalid"); + + // Return result + if (licensed) + return name; + } catch (Throwable ex) { + Util.bug(null, ex); + } + return null; + } + + @SuppressLint("NewApi") + public static int getAppId(int uid) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) + try { + // UserHandle: public static final int getAppId(int uid) + Method method = (Method) UserHandle.class.getDeclaredMethod("getAppId", int.class); + uid = (Integer) method.invoke(null, uid); + } catch (Throwable ex) { + Util.log(null, Log.WARN, ex.toString()); + } + return uid; + } + + @SuppressLint("NewApi") + public static int getUserId(int uid) { + int userId = 0; + if (uid > 99) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) + try { + // UserHandle: public static final int getUserId(int uid) + Method method = (Method) UserHandle.class.getDeclaredMethod("getUserId", int.class); + userId = (Integer) method.invoke(null, uid); + } catch (Throwable ex) { + Util.log(null, Log.WARN, ex.toString()); + } + } else + userId = uid; + return userId; + } + + public static String getUserDataDirectory(int uid) { + // Build data directory + String dataDir = Environment.getDataDirectory() + File.separator; + int userId = getUserId(uid); + if (userId == 0) + dataDir += "data"; + else + dataDir += "user" + File.separator + userId; + dataDir += File.separator + Util.class.getPackage().getName(); + return dataDir; + } + + public static String[] getProLicenseUnchecked() { + // Get license file name + String storageDir = Environment.getExternalStorageDirectory().getAbsolutePath(); + File licenseFile = new File(storageDir + File.separator + LICENSE_FILE_NAME); + if (!licenseFile.exists()) + licenseFile = new File(storageDir + File.separator + ".xprivacy" + File.separator + LICENSE_FILE_NAME); + if (!licenseFile.exists()) + licenseFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + + File.separator + LICENSE_FILE_NAME); + + String importedLicense = importProLicense(licenseFile); + if (importedLicense == null) + return null; + + // Check license file + licenseFile = new File(importedLicense); + if (licenseFile.exists()) { + // Read license + try { + IniFile iniFile = new IniFile(licenseFile); + String name = iniFile.get("name", ""); + String email = iniFile.get("email", ""); + String signature = iniFile.get("signature", ""); + if (name == null || email == null || signature == null) + return null; + else { + // Check expiry + if (email.endsWith("@faircode.eu")) { + long expiry = Long.parseLong(email.split("\\.")[0]); + long time = System.currentTimeMillis() / 1000L; + if (time > expiry) { + Util.log(null, Log.WARN, "Licensing: expired"); + return null; + } + } + + // Valid + return new String[] { name, email, signature }; + } + } catch (FileNotFoundException ex) { + return null; + } catch (Throwable ex) { + bug(null, ex); + return null; + } + } else + Util.log(null, Log.INFO, "Licensing: no license file"); + return null; + } + + public static String importProLicense(File licenseFile) { + // Get imported license file name + String importedLicense = getUserDataDirectory(Process.myUid()) + File.separator + LICENSE_FILE_NAME; + File out = new File(importedLicense); + + // Check if license file exists + if (licenseFile.exists() && licenseFile.canRead()) { + try { + // Import license file + Util.log(null, Log.WARN, "Licensing: importing " + out.getAbsolutePath()); + InputStream is = null; + is = new FileInputStream(licenseFile.getAbsolutePath()); + try { + OutputStream os = null; + try { + os = new FileOutputStream(out.getAbsolutePath()); + byte[] buffer = new byte[1024]; + int read; + while ((read = is.read(buffer)) != -1) + os.write(buffer, 0, read); + os.flush(); + } finally { + if (os != null) + os.close(); + } + } finally { + if (is != null) + is.close(); + } + + // Protect imported license file + setPermissions(out.getAbsolutePath(), 0700, Process.myUid(), Process.myUid()); + + // Remove original license file + licenseFile.delete(); + } catch (FileNotFoundException ignored) { + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + return (out.exists() && out.canRead() ? importedLicense : null); + } + + public static Version getProEnablerVersion(Context context) { + try { + String proPackageName = context.getPackageName() + ".pro"; + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(proPackageName, 0); + return new Version(pi.versionName); + } catch (NameNotFoundException ignored) { + } catch (Throwable ex) { + Util.bug(null, ex); + } + return null; + } + + public static boolean isValidProEnablerVersion(Version version) { + return (version.compareTo(MIN_PRO_VERSION) >= 0); + } + + private static boolean hasValidProEnablerSignature(Context context) { + return (context.getPackageManager() + .checkSignatures(context.getPackageName(), context.getPackageName() + ".pro") == PackageManager.SIGNATURE_MATCH); + } + + public static boolean isProEnablerInstalled(Context context) { + Version version = getProEnablerVersion(context); + if (version != null && isValidProEnablerVersion(version) && hasValidProEnablerSignature(context)) { + Util.log(null, Log.INFO, "Licensing: enabler installed"); + return true; + } + Util.log(null, Log.INFO, "Licensing: enabler not installed"); + return false; + } + + public static boolean hasMarketLink(Context context, String packageName) { + try { + PackageManager pm = context.getPackageManager(); + String installer = pm.getInstallerPackageName(packageName); + if (installer != null) + return installer.equals("com.android.vending") || installer.contains("google"); + } catch (Exception ex) { + log(null, Log.WARN, ex.toString()); + } + return false; + } + + public static void viewUri(Context context, Uri uri) { + Intent infoIntent = new Intent(Intent.ACTION_VIEW); + infoIntent.setData(uri); + if (isIntentAvailable(context, infoIntent)) + context.startActivity(infoIntent); + else + Toast.makeText(context, "View action not available", Toast.LENGTH_LONG).show(); + } + + public static boolean hasLBE() { + if (mHasLBE == null) { + mHasLBE = false; + try { + File apps = new File(Environment.getDataDirectory() + File.separator + "app"); + File[] files = (apps == null ? null : apps.listFiles()); + if (files != null) + for (File file : files) + if (file.getName().startsWith("com.lbe.security")) { + mHasLBE = true; + break; + } + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + return mHasLBE; + } + + public static boolean isSELinuxEnforced() { + try { + Class cSELinux = Class.forName("android.os.SELinux"); + if ((Boolean) cSELinux.getDeclaredMethod("isSELinuxEnabled").invoke(null)) + if ((Boolean) cSELinux.getDeclaredMethod("isSELinuxEnforced").invoke(null)) + return true; + } catch (Throwable t) { + } + return false; + } + + public static String getXOption(String name) { + try { + Class cSystemProperties = Class.forName("android.os.SystemProperties"); + Method spGet = cSystemProperties.getDeclaredMethod("get", String.class); + String options = (String) spGet.invoke(null, "xprivacy.options"); + Log.w("XPrivacy", "Options=" + options); + if (options != null) + for (String option : options.split(",")) { + String[] nv = option.split("="); + if (nv[0].equals(name)) + if (nv.length > 1) + return nv[1]; + else + return "true"; + } + } catch (Throwable ex) { + Log.e("XPrivacy", ex.toString() + "\n" + Log.getStackTraceString(ex)); + } + return null; + } + + public static int getSelfVersionCode(Context context) { + try { + String self = Util.class.getPackage().getName(); + PackageManager pm = context.getPackageManager(); + PackageInfo pInfo = pm.getPackageInfo(self, 0); + return pInfo.versionCode; + } catch (NameNotFoundException ex) { + Util.bug(null, ex); + return 0; + } + } + + public static String getSelfVersionName(Context context) { + try { + String self = Util.class.getPackage().getName(); + PackageManager pm = context.getPackageManager(); + PackageInfo pInfo = pm.getPackageInfo(self, 0); + return pInfo.versionName; + } catch (NameNotFoundException ex) { + Util.bug(null, ex); + return null; + } + } + + private static byte[] hex2bytes(String hex) { + // Convert hex string to byte array + int len = hex.length(); + byte[] result = new byte[len / 2]; + for (int i = 0; i < len; i += 2) + result[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); + return result; + } + + private static PublicKey getPublicKey(Context context) throws Throwable { + // Read public key + String sPublicKey = ""; + InputStreamReader isr = new InputStreamReader(context.getAssets().open("XPrivacy_public_key.txt"), "UTF-8"); + BufferedReader br = new BufferedReader(isr); + String line = br.readLine(); + while (line != null) { + if (!line.startsWith("-----")) + sPublicKey += line; + line = br.readLine(); + } + br.close(); + isr.close(); + + // Create public key + byte[] bPublicKey = Base64.decode(sPublicKey, Base64.NO_WRAP); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + X509EncodedKeySpec encodedPubKeySpec = new X509EncodedKeySpec(bPublicKey); + return keyFactory.generatePublic(encodedPubKeySpec); + } + + private static boolean verifyData(byte[] data, byte[] signature, PublicKey publicKey) throws Throwable { + // Verify signature + Signature verifier = Signature.getInstance("SHA1withRSA"); + verifier.initVerify(publicKey); + verifier.update(data); + return verifier.verify(signature); + } + + public static String sha1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException { + // SHA1 + int userId = Util.getUserId(Process.myUid()); + String salt = PrivacyManager.getSalt(userId); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + byte[] bytes = (text + salt).getBytes("UTF-8"); + digest.update(bytes, 0, bytes.length); + bytes = digest.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) + sb.append(String.format("%02X", b)); + return sb.toString(); + } + + public static String md5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException { + // MD5 + int userId = Util.getUserId(Process.myUid()); + String salt = PrivacyManager.getSalt(userId); + byte[] bytes = MessageDigest.getInstance("MD5").digest((text + salt).getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) + sb.append(String.format("%02X", b)); + return sb.toString(); + } + + @SuppressLint("DefaultLocale") + public static boolean hasValidFingerPrint(Context context) { + try { + PackageManager pm = context.getPackageManager(); + String packageName = context.getPackageName(); + PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + byte[] cert = packageInfo.signatures[0].toByteArray(); + MessageDigest digest = MessageDigest.getInstance("SHA1"); + byte[] bytes = digest.digest(cert); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; ++i) + sb.append((Integer.toHexString((bytes[i] & 0xFF) | 0x100)).substring(1, 3).toLowerCase()); + String calculated = sb.toString(); + String expected = context.getString(R.string.fingerprint); + return calculated.equals(expected); + } catch (Throwable ex) { + bug(null, ex); + return false; + } + } + + public static boolean isDebuggable(Context context) { + return ((context.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + } + + public static boolean isIntentAvailable(Context context, Intent intent) { + PackageManager packageManager = context.getPackageManager(); + return (packageManager.queryIntentActivities(intent, PackageManager.GET_ACTIVITIES).size() > 0); + } + + public static void setPermissions(String path, int mode, int uid, int gid) { + try { + // frameworks/base/core/java/android/os/FileUtils.java + Class fileUtils = Class.forName("android.os.FileUtils"); + Method setPermissions = fileUtils + .getMethod("setPermissions", String.class, int.class, int.class, int.class); + setPermissions.invoke(null, path, mode, uid, gid); + Util.log(null, Log.WARN, "Changed permission path=" + path + " mode=" + Integer.toOctalString(mode) + + " uid=" + uid + " gid=" + gid); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public static void copy(File src, File dst) throws IOException { + FileInputStream inStream = null; + try { + inStream = new FileInputStream(src); + FileOutputStream outStream = null; + try { + outStream = new FileOutputStream(dst); + FileChannel inChannel = inStream.getChannel(); + FileChannel outChannel = outStream.getChannel(); + inChannel.transferTo(0, inChannel.size(), outChannel); + } finally { + if (outStream != null) + outStream.close(); + } + } finally { + if (inStream != null) + inStream.close(); + } + } + + public static boolean move(File src, File dst) { + try { + copy(src, dst); + } catch (IOException ex) { + Util.bug(null, ex); + return false; + } + return src.delete(); + } + + public static List getViewsByTag(ViewGroup root, String tag) { + List views = new ArrayList(); + for (int i = 0; i < root.getChildCount(); i++) { + View child = root.getChildAt(i); + + if (child instanceof ViewGroup) + views.addAll(getViewsByTag((ViewGroup) child, tag)); + + if (tag.equals(child.getTag())) + views.add(child); + } + return views; + } + + public static float dipToPixels(Context context, float dipValue) { + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics); + } + + public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + final int halfHeight = height / 2; + final int halfWidth = width / 2; + while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) + inSampleSize *= 2; + } + + return inSampleSize; + } + + public static String getSEContext() { + try { + Class cSELinux = Class.forName("android.os.SELinux"); + Method mGetContext = cSELinux.getDeclaredMethod("getContext"); + return (String) mGetContext.invoke(null); + } catch (Throwable ex) { + Util.bug(null, ex); + return null; + } + + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Version.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Version.java new file mode 100644 index 0000000..3967e99 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/Version.java @@ -0,0 +1,35 @@ +package biz.bokhorst.xprivacy; + +public class Version implements Comparable { + + private String mVersion; + + public Version(String version) { + mVersion = version; + } + + private String get() { + return mVersion; + } + + @Override + public int compareTo(Version other) { + String[] lhs = this.get().split("\\."); + String[] rhs = other.get().split("\\."); + int length = Math.max(lhs.length, rhs.length); + for (int i = 0; i < length; i++) { + int vLhs = (i < lhs.length ? Integer.parseInt(lhs[i]) : 0); + int vRhs = (i < rhs.length ? Integer.parseInt(rhs[i]) : 0); + if (vLhs < vRhs) + return -1; + if (vLhs > vRhs) + return 1; + } + return 0; + } + + @Override + public String toString() { + return mVersion; + } +} \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/WhitelistAdapter.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/WhitelistAdapter.java new file mode 100644 index 0000000..3dc40a8 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/WhitelistAdapter.java @@ -0,0 +1,95 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.CheckedTextView; +import android.widget.ImageView; + +@SuppressLint("DefaultLocale") +public class WhitelistAdapter extends ArrayAdapter { + private String mSelectedType; + private int mUid; + private Map> mMapWhitelists; + private LayoutInflater mInflater; + + public WhitelistAdapter(Context context, int resource, int uid, Map> mapWhitelists) { + super(context, resource, new ArrayList()); + mUid = uid; + mMapWhitelists = mapWhitelists; + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + public void setType(String selectedType) { + mSelectedType = selectedType; + this.clear(); + if (mMapWhitelists.containsKey(selectedType)) + this.addAll(mMapWhitelists.get(selectedType).keySet()); + } + + private class ViewHolder { + private View row; + public CheckedTextView ctvName; + public ImageView imgDelete; + + public ViewHolder(View theRow, int thePosition) { + row = theRow; + ctvName = (CheckedTextView) row.findViewById(R.id.cbName); + imgDelete = (ImageView) row.findViewById(R.id.imgDelete); + } + } + + @Override + @SuppressLint("InflateParams") + public View getView(int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.whitelistentry, null); + holder = new ViewHolder(convertView, position); + convertView.setTag(holder); + } else + holder = (ViewHolder) convertView.getTag(); + + // Set data + final String name = this.getItem(position); + holder.ctvName.setText(name); + holder.ctvName.setChecked(mMapWhitelists.get(mSelectedType).get(name)); + + final boolean wnomod = PrivacyManager.getSettingBool(mUid, PrivacyManager.cSettingWhitelistNoModify, false); + + holder.ctvName.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + // Toggle white/black list entry + holder.ctvName.toggle(); + boolean isChecked = holder.ctvName.isChecked(); + mMapWhitelists.get(mSelectedType).put(name, isChecked); + PrivacyManager.setSetting(mUid, mSelectedType, name, Boolean.toString(isChecked)); + if (!wnomod) + PrivacyManager.updateState(mUid); + } + }); + + holder.imgDelete.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + // Delete white/black list entry + WhitelistAdapter.this.remove(name); + mMapWhitelists.get(mSelectedType).remove(name); + PrivacyManager.setSetting(mUid, mSelectedType, name, null); + if (!wnomod) + PrivacyManager.updateState(mUid); + } + }); + + return convertView; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/WhitelistTask.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/WhitelistTask.java new file mode 100644 index 0000000..5e13bd4 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/WhitelistTask.java @@ -0,0 +1,111 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.AdapterView.OnItemSelectedListener; + +public class WhitelistTask extends AsyncTask { + private int mUid; + private String mType; + private ActivityBase mContext; + private WhitelistAdapter mWhitelistAdapter; + private Map> mListWhitelist; + + public WhitelistTask(int uid, String type, ActivityBase context) { + mUid = uid; + mType = type; + mContext = context; + } + + @Override + protected Object doInBackground(Object... params) { + mListWhitelist = PrivacyManager.listWhitelisted(mUid, null); + mWhitelistAdapter = new WhitelistAdapter(mContext, R.layout.whitelistentry, mUid, mListWhitelist); + return null; + } + + @Override + @SuppressLint({ "DefaultLocale", "InflateParams" }) + protected void onPostExecute(Object result) { + if (!mContext.isFinishing()) { + // Build dialog + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mContext); + alertDialogBuilder.setTitle(R.string.menu_whitelists); + alertDialogBuilder.setIcon(mContext.getThemed(R.attr.icon_launcher)); + + if (mListWhitelist.keySet().size() > 0) { + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View llWhitelists = inflater.inflate(R.layout.whitelists, null); + + int index = 0; + int selected = -1; + final List localizedType = new ArrayList(); + for (String type : mListWhitelist.keySet()) { + String name = "whitelist_" + type.toLowerCase().replace("/", ""); + int id = mContext.getResources().getIdentifier(name, "string", mContext.getPackageName()); + if (id == 0) + localizedType.add(name); + else + localizedType.add(mContext.getResources().getString(id)); + + if (type.equals(mType)) + selected = index; + index++; + } + + Spinner spWhitelistType = (Spinner) llWhitelists.findViewById(R.id.spWhitelistType); + ArrayAdapter whitelistTypeAdapter = new ArrayAdapter(mContext, + android.R.layout.simple_spinner_dropdown_item, localizedType); + spWhitelistType.setAdapter(whitelistTypeAdapter); + spWhitelistType.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mWhitelistAdapter.setType(mListWhitelist.keySet().toArray(new String[0])[position]); + } + + @Override + public void onNothingSelected(AdapterView view) { + } + }); + if (selected >= 0) + spWhitelistType.setSelection(selected); + + ListView lvWhitelist = (ListView) llWhitelists.findViewById(R.id.lvWhitelist); + lvWhitelist.setAdapter(mWhitelistAdapter); + int position = spWhitelistType.getSelectedItemPosition(); + if (position != AdapterView.INVALID_POSITION) + mWhitelistAdapter.setType(mListWhitelist.keySet().toArray(new String[0])[position]); + + alertDialogBuilder.setView(llWhitelists); + } + + alertDialogBuilder.setPositiveButton(mContext.getString(R.string.msg_done), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }); + + // Show dialog + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + + super.onPostExecute(result); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAccountManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAccountManager.java new file mode 100644 index 0000000..55f37fc --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAccountManager.java @@ -0,0 +1,533 @@ +package biz.bokhorst.xprivacy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.TimeUnit; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorDescription; +import android.accounts.AuthenticatorException; +import android.accounts.OnAccountsUpdateListener; +import android.accounts.OperationCanceledException; +import android.os.Binder; +import android.os.Bundle; +import android.util.Log; + +public class XAccountManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.accounts.AccountManager"; + private static final Map mListener = new WeakHashMap(); + + private XAccountManager(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + mClassName = "com.android.server.accounts.AccountManagerService"; + } + + private XAccountManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener, Handler handler, boolean updateImmediately) + // public String blockingGetAuthToken(Account account, String authTokenType, boolean notifyAuthFailure) + // public Account[] getAccounts() + // public Account[] getAccountsByType(String type) + // public Account[] getAccountsByTypeForPackage(String type, String packageName) + // public AccountManagerFuture getAccountsByTypeAndFeatures(final String type, final String[] features, AccountManagerCallback callback, Handler handler) + // public AuthenticatorDescription[] getAuthenticatorTypes() + // public AccountManagerFuture getAuthToken(final Account account, final String authTokenType, final Bundle options, final Activity activity, AccountManagerCallback callback, Handler handler) + // public AccountManagerFuture getAuthToken(final Account account, final String authTokenType, final boolean notifyAuthFailure, AccountManagerCallback callback, Handler handler) + // public AccountManagerFuture getAuthToken(final Account account, final String authTokenType, final Bundle options, final boolean notifyAuthFailure, AccountManagerCallback callback, Handler handler) + // public AccountManagerFuture getAuthTokenByFeatures(final String accountType, final String authTokenType, final String[] features, final Activity activity, final Bundle addAccountOptions, final Bundle getAuthTokenOptions, final AccountManagerCallback callback, final Handler handler) + // public AccountManagerFuture hasFeatures(final Account account, final String[] features, AccountManagerCallback callback, Handler handler) + // public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) + // frameworks/base/core/java/android/accounts/AccountManager.java + // http://developer.android.com/reference/android/accounts/AccountManager.html + + // @formatter:on + + // @formatter:off + + // public android.accounts.Account[] getAccounts(java.lang.String accountType) throws android.os.RemoteException; + // public android.accounts.Account[] getAccountsAsUser(java.lang.String accountType, int userId) throws android.os.RemoteException; + // public void getAccountsByFeatures(android.accounts.IAccountManagerResponse response, java.lang.String accountType, java.lang.String[] features) throws android.os.RemoteException; + // public android.accounts.Account[] getAccountsForPackage(java.lang.String packageName, int uid) + // public android.accounts.Account[] getSharedAccountsAsUser(int userId) throws android.os.RemoteException; + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/server/accounts/AccountManagerService.java + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/accounts/IAccountManager.java + + // @formatter:on + + // @formatter:off + private enum Methods { + addOnAccountsUpdatedListener, + blockingGetAuthToken, + getAccounts, getAccountsByType, getAccountsByTypeForPackage, getAccountsByTypeAndFeatures, getAuthenticatorTypes, + getAuthToken, getAuthTokenByFeatures, + hasFeatures, + removeOnAccountsUpdatedListener, + + Srv_getAccounts, + Srv_getAccountsAsUser, + Srv_getAccountsByFeatures, + Srv_getAccountsForPackage, + Srv_getSharedAccountsAsUser + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + if (server) { + listHook.add(new XAccountManager(Methods.Srv_getAccounts, PrivacyManager.cAccounts)); + listHook.add(new XAccountManager(Methods.Srv_getAccountsAsUser, PrivacyManager.cAccounts)); + listHook.add(new XAccountManager(Methods.Srv_getAccountsByFeatures, PrivacyManager.cAccounts)); + listHook.add(new XAccountManager(Methods.Srv_getAccountsForPackage, PrivacyManager.cAccounts)); + listHook.add(new XAccountManager(Methods.Srv_getSharedAccountsAsUser, PrivacyManager.cAccounts)); + } else { + listHook.add(new XAccountManager(Methods.addOnAccountsUpdatedListener, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.blockingGetAuthToken, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAccounts, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAccountsByType, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAccountsByTypeForPackage, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAccountsByTypeAndFeatures, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAuthenticatorTypes, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAuthToken, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.getAuthTokenByFeatures, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.hasFeatures, PrivacyManager.cAccounts, className)); + listHook.add(new XAccountManager(Methods.removeOnAccountsUpdatedListener, null, className)); + } + } + return listHook; + } + + @Override + @SuppressWarnings("unchecked") + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case addOnAccountsUpdatedListener: + if (param.args.length > 0 && param.args[0] != null) + if (isRestricted(param)) { + int uid = Binder.getCallingUid(); + OnAccountsUpdateListener listener = (OnAccountsUpdateListener) param.args[0]; + XOnAccountsUpdateListener xListener; + synchronized (mListener) { + xListener = mListener.get(listener); + if (xListener == null) { + xListener = new XOnAccountsUpdateListener(listener, uid); + mListener.put(listener, xListener); + Util.log(this, Log.WARN, "Added count=" + mListener.size() + " uid=" + uid); + } + } + param.args[0] = xListener; + } + break; + + case blockingGetAuthToken: + case getAccounts: + case getAccountsByType: + case getAccountsByTypeForPackage: + // Do nothing + break; + + case getAccountsByTypeAndFeatures: + if (param.args.length > 2 && param.args[2] != null) + if (isRestrictedExtra(param, (String) param.args[0])) { + AccountManagerCallback callback = (AccountManagerCallback) param.args[2]; + param.args[2] = new XAccountManagerCallbackAccount(callback, Binder.getCallingUid()); + } + break; + + case getAuthenticatorTypes: + // Do nothing + break; + + case getAuthToken: + if (param.args.length > 0) { + Account account = (Account) param.args[0]; + for (int i = 1; i < param.args.length; i++) + if (param.args[i] instanceof AccountManagerCallback) + if (isRestrictedExtra(param, account == null ? null : account.name)) { + AccountManagerCallback callback = (AccountManagerCallback) param.args[i]; + param.args[i] = new XAccountManagerCallbackBundle(callback, Binder.getCallingUid()); + } + } + break; + + case getAuthTokenByFeatures: + if (param.args.length > 0) + for (int i = 1; i < param.args.length; i++) + if (param.args[i] instanceof AccountManagerCallback) + if (isRestrictedExtra(param, (String) param.args[0])) { + AccountManagerCallback callback = (AccountManagerCallback) param.args[i]; + param.args[i] = new XAccountManagerCallbackBundle(callback, Binder.getCallingUid()); + } + break; + + case hasFeatures: + if (param.args.length > 0) { + Account account = (Account) param.args[0]; + for (int i = 1; i < param.args.length; i++) + if (param.args[i] instanceof AccountManagerCallback) + if (isRestrictedExtra(param, account == null ? null : account.name)) { + AccountManagerCallback callback = (AccountManagerCallback) param.args[i]; + param.args[i] = new XAccountManagerCallbackBoolean(callback); + } + } + break; + + case removeOnAccountsUpdatedListener: + if (param.args.length > 0 && param.args[0] != null) + synchronized (mListener) { + OnAccountsUpdateListener listener = (OnAccountsUpdateListener) param.args[0]; + XOnAccountsUpdateListener xListener = mListener.get(listener); + if (xListener != null) { + param.args[0] = xListener; + Util.log(this, Log.WARN, "Removed count=" + mListener.size() + " uid=" + Binder.getCallingUid()); + } + } + break; + + case Srv_getAccounts: + case Srv_getAccountsAsUser: + case Srv_getAccountsForPackage: + // Do nothing + break; + + case Srv_getAccountsByFeatures: + if (param.args.length > 1 && (param.args[1] == null || param.args[1] instanceof String)) { + if (isRestrictedExtra(param, (String) param.args[1])) + param.setResult(null); + } else { + if (isRestricted(param)) + param.setResult(null); + } + break; + + case Srv_getSharedAccountsAsUser: + // Do nothing + break; + } + } + + @Override + @SuppressWarnings("unchecked") + protected void after(XParam param) throws Throwable { + int uid = Binder.getCallingUid(); + + switch (mMethod) { + case addOnAccountsUpdatedListener: + // Do nothing + break; + + case blockingGetAuthToken: + if (param.getResult() != null && param.args.length > 0 && param.args[0] != null) { + Account account = (Account) param.args[0]; + if (isRestrictedExtra(param, account == null ? null : account.name)) + if (!isAccountAllowed(account, uid)) + param.setResult(null); + } + break; + + case getAccounts: + if (param.getResult() != null && isRestricted(param)) { + Account[] accounts = (Account[]) param.getResult(); + param.setResult(filterAccounts(accounts, uid)); + } + break; + + case getAccountsByType: + case getAccountsByTypeForPackage: + if (param.getResult() != null && param.args.length > 0) + if (isRestrictedExtra(param, (String) param.args[0])) { + Account[] accounts = (Account[]) param.getResult(); + param.setResult(filterAccounts(accounts, uid)); + } + break; + + case getAccountsByTypeAndFeatures: + if (param.getResult() != null && param.args.length > 0) + if (isRestrictedExtra(param, (String) param.args[0])) { + AccountManagerFuture future = (AccountManagerFuture) param.getResult(); + param.setResult(new XFutureAccount(future, uid)); + } + break; + + case getAuthenticatorTypes: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new AuthenticatorDescription[0]); + break; + + case getAuthToken: + if (param.getResult() != null && param.args.length > 0) { + Account account = (Account) param.args[0]; + if (isRestrictedExtra(param, account == null ? null : account.name)) { + AccountManagerFuture future = (AccountManagerFuture) param.getResult(); + param.setResult(new XFutureBundle(future, uid)); + } + } + break; + + case getAuthTokenByFeatures: + if (param.getResult() != null) + if (isRestrictedExtra(param, (String) param.args[0])) { + AccountManagerFuture future = (AccountManagerFuture) param.getResult(); + param.setResult(new XFutureBundle(future, uid)); + } + break; + + case hasFeatures: + if (param.getResult() != null && param.args.length > 0 && param.args[0] != null) { + Account account = (Account) param.args[0]; + if (isRestrictedExtra(param, account == null ? null : account.name)) + if (!isAccountAllowed(account, uid)) + param.setResult(new XFutureBoolean()); + } + break; + + case removeOnAccountsUpdatedListener: + // Do nothing + break; + + case Srv_getAccounts: + case Srv_getAccountsAsUser: + case Srv_getAccountsForPackage: + case Srv_getSharedAccountsAsUser: + // Filter account list + String extra = null; + if (mMethod == Methods.Srv_getAccounts || mMethod == Methods.Srv_getAccountsAsUser + || mMethod == Methods.Srv_getAccountsForPackage) + if (param.args.length > 0 && param.args[0] instanceof String) + extra = (String) param.args[0]; + + if (param.getResult() instanceof Account[]) + if (isRestrictedExtra(param, extra)) { + Account[] accounts = (Account[]) param.getResult(); + param.setResult(filterAccounts(accounts, uid)); + } + break; + + case Srv_getAccountsByFeatures: + // Do nothing + break; + } + } + + private Account[] filterAccounts(Account[] original, int uid) { + List listAccount = new ArrayList(); + for (Account account : original) + if (isAccountAllowed(account, uid)) + listAccount.add(account); + return listAccount.toArray(new Account[0]); + } + + public static boolean isAccountAllowed(Account account, int uid) { + return isAccountAllowed(account.name, account.type, uid); + } + + public static boolean isAccountAllowed(String accountName, String accountType, int uid) { + try { + boolean allowed = PrivacyManager.getSettingBool(-uid, Meta.cTypeAccountHash, accountName + accountType, + false); + boolean blacklist = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingBlacklist, false); + if (blacklist) + allowed = !allowed; + return allowed; + } catch (Throwable ex) { + Util.bug(null, ex); + return false; + } + } + + private class XFutureAccount implements AccountManagerFuture { + private AccountManagerFuture mFuture; + private int mUid; + + public XFutureAccount(AccountManagerFuture future, int uid) { + mFuture = future; + mUid = uid; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return mFuture.cancel(mayInterruptIfRunning); + } + + @Override + public Account[] getResult() throws OperationCanceledException, IOException, AuthenticatorException { + Account[] account = mFuture.getResult(); + return XAccountManager.this.filterAccounts(account, mUid); + } + + @Override + public Account[] getResult(long timeout, TimeUnit unit) throws OperationCanceledException, IOException, + AuthenticatorException { + Account[] account = mFuture.getResult(timeout, unit); + return XAccountManager.this.filterAccounts(account, mUid); + } + + @Override + public boolean isCancelled() { + return mFuture.isCancelled(); + } + + @Override + public boolean isDone() { + return mFuture.isDone(); + } + } + + private class XFutureBoolean implements AccountManagerFuture { + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public Boolean getResult() throws OperationCanceledException, IOException, AuthenticatorException { + return false; + } + + @Override + public Boolean getResult(long timeout, TimeUnit unit) throws OperationCanceledException, IOException, + AuthenticatorException { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + } + + private class XFutureBundle implements AccountManagerFuture { + + private AccountManagerFuture mFuture; + private int mUid; + + public XFutureBundle(AccountManagerFuture future, int uid) { + mFuture = future; + mUid = uid; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return mFuture.cancel(mayInterruptIfRunning); + } + + @Override + public Bundle getResult() throws OperationCanceledException, IOException, AuthenticatorException { + Bundle bundle = mFuture.getResult(); + String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = bundle.getString(AccountManager.KEY_ACCOUNT_TYPE); + if (isAccountAllowed(accountName, accountType, mUid)) + return bundle; + else + throw new OperationCanceledException("XPrivacy"); + } + + @Override + public Bundle getResult(long timeout, TimeUnit unit) throws OperationCanceledException, IOException, + AuthenticatorException { + Bundle bundle = mFuture.getResult(timeout, unit); + String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = bundle.getString(AccountManager.KEY_ACCOUNT_TYPE); + if (isAccountAllowed(accountName, accountType, mUid)) + return bundle; + else + throw new OperationCanceledException("XPrivacy"); + } + + @Override + public boolean isCancelled() { + return mFuture.isCancelled(); + } + + @Override + public boolean isDone() { + return mFuture.isDone(); + } + } + + private class XOnAccountsUpdateListener implements OnAccountsUpdateListener { + private OnAccountsUpdateListener mListener; + private int mUid; + + public XOnAccountsUpdateListener(OnAccountsUpdateListener listener, int uid) { + mListener = listener; + mUid = uid; + } + + @Override + public void onAccountsUpdated(Account[] accounts) { + mListener.onAccountsUpdated(XAccountManager.this.filterAccounts(accounts, mUid)); + } + } + + private class XAccountManagerCallbackAccount implements AccountManagerCallback { + private AccountManagerCallback mCallback; + private int mUid; + + public XAccountManagerCallbackAccount(AccountManagerCallback callback, int uid) { + mCallback = callback; + mUid = uid; + } + + @Override + public void run(AccountManagerFuture future) { + mCallback.run(new XAccountManager.XFutureAccount(future, mUid)); + } + } + + private class XAccountManagerCallbackBundle implements AccountManagerCallback { + private AccountManagerCallback mCallback; + private int mUid; + + public XAccountManagerCallbackBundle(AccountManagerCallback callback, int uid) { + mCallback = callback; + mUid = uid; + } + + @Override + public void run(AccountManagerFuture future) { + mCallback.run(new XAccountManager.XFutureBundle(future, mUid)); + } + } + + private class XAccountManagerCallbackBoolean implements AccountManagerCallback { + private AccountManagerCallback mCallback; + + public XAccountManagerCallbackBoolean(AccountManagerCallback callback) { + mCallback = callback; + } + + @Override + public void run(AccountManagerFuture future) { + mCallback.run(new XAccountManager.XFutureBoolean()); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivity.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivity.java new file mode 100644 index 0000000..480abc3 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivity.java @@ -0,0 +1,144 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.net.Uri; +import android.provider.MediaStore; + +public class XActivity extends XHook { + private Methods mMethod; + private String mActionName; + + private XActivity(Methods method, String restrictionName, String actionName) { + super(restrictionName, method.name(), actionName); + mMethod = method; + mActionName = actionName; + } + + public String getClassName() { + return "android.app.Activity"; + } + + // @formatter:off + + // public Object getSystemService(String name) + // public void startActivities(Intent[] intents) + // public void startActivities(Intent[] intents, Bundle options) + // public void startActivity(Intent intent) + // public void startActivity(Intent intent, Bundle options) + // public void startActivityForResult(Intent intent, int requestCode) + // public void startActivityForResult(Intent intent, int requestCode, Bundle options) + // public void startActivityFromChild(Activity child, Intent intent, int requestCode) + // public void startActivityFromChild(Activity child, Intent intent, int requestCode, Bundle options) + // public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) + // public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options) + // public boolean startActivityIfNeeded(Intent intent, int requestCode) + // public boolean startActivityIfNeeded(Intent intent, int requestCode, Bundle options) + // public boolean startNextMatchingActivity(Intent intent) + // public boolean startNextMatchingActivity(Intent intent, Bundle options) + // frameworks/base/core/java/android/app/Activity.java + + // @formatter:on + + // @formatter:off + private enum Methods { + getSystemService, + startActivities, startActivity, startActivityForResult, startActivityFromChild, startActivityFromFragment, startActivityIfNeeded, startNextMatchingActivity + }; + // @formatter:on + + @SuppressLint("InlinedApi") + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XActivity(Methods.getSystemService, null, null)); + + List startMethods = new ArrayList(Arrays.asList(Methods.values())); + startMethods.remove(Methods.getSystemService); + + // Intent send: browser + for (Methods activity : startMethods) + listHook.add(new XActivity(activity, PrivacyManager.cView, Intent.ACTION_VIEW)); + + // Intent send: call/dial + for (Methods activity : startMethods) { + listHook.add(new XActivity(activity, PrivacyManager.cCalling, Intent.ACTION_CALL)); + listHook.add(new XActivity(activity, PrivacyManager.cCalling, Intent.ACTION_DIAL)); + } + + // Intent send: media + for (Methods activity : startMethods) { + listHook.add(new XActivity(activity, PrivacyManager.cMedia, MediaStore.ACTION_IMAGE_CAPTURE)); + listHook.add(new XActivity(activity, PrivacyManager.cMedia, MediaStore.ACTION_IMAGE_CAPTURE_SECURE)); + listHook.add(new XActivity(activity, PrivacyManager.cMedia, MediaStore.ACTION_VIDEO_CAPTURE)); + } + + return listHook; + } + + @Override + @SuppressLint("DefaultLocale") + protected void before(XParam param) throws Throwable { + // Get intent(s) + Intent[] intents = null; + switch (mMethod) { + case getSystemService: + // Do nothing + break; + + case startActivity: + case startActivityForResult: + case startActivityIfNeeded: + case startNextMatchingActivity: + if (param.args.length > 0 && param.args[0] instanceof Intent) + intents = new Intent[] { (Intent) param.args[0] }; + break; + + case startActivityFromChild: + case startActivityFromFragment: + if (param.args.length > 1 && param.args[1] instanceof Intent) + intents = new Intent[] { (Intent) param.args[1] }; + break; + + case startActivities: + if (param.args.length > 0 && param.args[0] instanceof Intent[]) + intents = (Intent[]) param.args[0]; + break; + } + + // Process intent(s) + if (intents != null) + for (Intent intent : intents) + if (mActionName.equals(intent.getAction())) { + boolean restricted = false; + if (mActionName.equals(Intent.ACTION_VIEW)) { + Uri uri = intent.getData(); + if (uri != null) + if (isRestrictedExtra(param, mActionName, uri.toString())) + restricted = true; + } else + restricted = isRestricted(param, mActionName); + + if (restricted) { + if (mMethod == Methods.startActivityIfNeeded) + param.setResult(true); + else + param.setResult(null); + return; + } + } + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.getSystemService) + if (param.args.length > 0 && param.args[0] instanceof String && param.getResult() != null) { + String name = (String) param.args[0]; + Object instance = param.getResult(); + XPrivacy.handleGetSystemService(name, instance.getClass().getName(), getSecret()); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityManager.java new file mode 100644 index 0000000..391f125 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityManager.java @@ -0,0 +1,198 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Intent; +import android.net.Uri; + +public class XActivityManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.app.ActivityManager"; + private static Map mapIntentRestriction = new HashMap(); + + static { + mapIntentRestriction.put(Intent.ACTION_VIEW, PrivacyManager.cView); + } + + private XActivityManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + if (className == null) + mClassName = "com.android.server.am.ActivityManagerService"; + else + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public List getRecentTasks(int maxNum, int flags) + // public List getRunningAppProcesses() + // public List getRunningServices(int maxNum) + // public List getRunningTasks(int maxNum) + // frameworks/base/core/java/android/app/ActivityManager.java + // http://developer.android.com/reference/android/app/ActivityManager.html + + // public List getRecentTasks(int maxNum, int flags, int userId) + // public List getRunningAppProcesses() + // public List getServices(int maxNum, int flags) + // public List getTasks(int maxNum, int flags, IThumbnailReceiver receiver) + + // public int startActivities(IApplicationThread caller, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options, int userId) + // public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) + // public int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, String profileFile,ParcelFileDescriptor profileFd, Bundle options, int userId) + // public WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) + // public int startActivityWithConfig(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Configuration newConfig, Bundle options, int userId) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/server/am/ActivityManagerService.java + // public int startActivityAsCaller(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags, ProfilerInfo profilerInfo, Bundle options, int userId) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/accounts/IAccountManager.java/ + + // @formatter:on + + // @formatter:off + private enum Methods { + getRecentTasks, getRunningAppProcesses, getRunningServices, getRunningTasks, + Srv_getRecentTasks, Srv_getRunningAppProcesses, Srv_getServices, Srv_getTasks, + Srv_startActivities, Srv_startActivity, Srv_startActivityAsCaller, Srv_startActivityAsUser, Srv_startActivityAndWait, Srv_startActivityWithConfig + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + for (Methods act : Methods.values()) + if (act.name().startsWith("Srv_")) { + if (server) + if (act.name().startsWith("Srv_start")) + listHook.add(new XActivityManager(act, null, null)); + else + listHook.add(new XActivityManager(act, PrivacyManager.cSystem, null)); + } else if (!server) + listHook.add(new XActivityManager(act, PrivacyManager.cSystem, className)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getRecentTasks: + case getRunningAppProcesses: + case getRunningServices: + case getRunningTasks: + case Srv_getRecentTasks: + case Srv_getRunningAppProcesses: + case Srv_getServices: + case Srv_getTasks: + break; + + case Srv_startActivities: + if (param.args.length > 2 && param.args[2] instanceof Intent[]) { + List listIntent = new ArrayList(); + for (Intent intent : (Intent[]) param.args[2]) + if (!isRestricted(param, intent)) + listIntent.add(intent); + if (listIntent.size() == 0) + param.setResult(0); // ActivityManager.START_SUCCESS + else + param.args[2] = listIntent.toArray(new Intent[0]); + } + break; + + case Srv_startActivity: + case Srv_startActivityAsCaller: + case Srv_startActivityAsUser: + case Srv_startActivityWithConfig: + if (param.args.length > 2 && param.args[2] instanceof Intent) { + Intent intent = (Intent) param.args[2]; + if (isRestricted(param, intent)) + param.setResult(0); // ActivityManager.START_SUCCESS + } + break; + + case Srv_startActivityAndWait: + if (param.args.length > 2 && param.args[2] instanceof Intent) { + Intent intent = (Intent) param.args[2]; + if (isRestricted(param, intent)) { + Class cWaitResult = Class.forName("android.app.IActivityManager.WaitResult"); + Field fWho = cWaitResult.getDeclaredField("who"); + Class we = this.getClass(); + ComponentName component = new ComponentName(we.getPackage().getName(), we.getName()); + + Object waitResult = cWaitResult.getConstructor().newInstance(); + fWho.set(waitResult, component); + param.setResult(waitResult); + } + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getRecentTasks: + case Srv_getRecentTasks: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getRunningAppProcesses: + case Srv_getRunningAppProcesses: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getRunningServices: + case Srv_getServices: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getRunningTasks: + case Srv_getTasks: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case Srv_startActivities: + case Srv_startActivity: + case Srv_startActivityAsCaller: + case Srv_startActivityAsUser: + case Srv_startActivityAndWait: + case Srv_startActivityWithConfig: + break; + } + } + + // Helper methods + + private boolean isRestricted(XParam param, Intent intent) throws Throwable { + String action = intent.getAction(); + if (mapIntentRestriction.containsKey(action)) { + String restrictionName = mapIntentRestriction.get(action); + if (Intent.ACTION_VIEW.equals(action)) { + Uri uri = intent.getData(); + if (uri != null) + return isRestrictedExtra(param, restrictionName, "Srv_" + action, uri.toString()); + } else + return isRestricted(param, restrictionName, "Srv_" + action); + } + + return false; + } + +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityManagerService.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityManagerService.java new file mode 100644 index 0000000..321cc43 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityManagerService.java @@ -0,0 +1,219 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; + +import android.annotation.SuppressLint; +import android.os.Build; +import android.util.Log; + +@SuppressLint("InlinedApi") +public class XActivityManagerService extends XHook { + private Methods mMethod; + + private static Semaphore mOndemandSemaphore; + private static boolean mFinishedBooting = false; + private static boolean mLockScreen = false; + private static boolean mSleeping = false; + private static boolean mShutdown = false; + + private XActivityManagerService(Methods method) { + super(null, method.name(), null); + mMethod = method; + } + + @Override + public boolean isVisible() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + if (mMethod == Methods.goingToSleep || mMethod == Methods.wakingUp) + return false; + + return (mMethod != Methods.appNotResponding && mMethod != Methods.finishBooting && mMethod != Methods.updateSleepIfNeededLocked); + } + + public String getClassName() { + return "com.android.server.am.ActivityManagerService"; + } + + // @formatter:off + + // 4.2+ public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) + // 4.3+ public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason) + // 4.0.3+ final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) + // 4.0.3+ public void systemReady(final Runnable goingCallback) + // 4.0.3+ final void finishBooting() + // 4.1+ public void setLockScreenShown(boolean shown) + // 4.0.3-5.0.x public void goingToSleep() + // 4.0.3-5.0.x public void wakingUp() + // 4.0.3+ public boolean shutdown(int timeout) + // 4.2+ public final void activityResumed(IBinder token) + // public final void activityPaused(IBinder token) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/com/android/server/am/ActivityManagerService.java/ + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/com/android/server/am/ActivityRecord.java/ + + // @formatter:on + + // @formatter:off + private enum Methods { + inputDispatchingTimedOut, appNotResponding, + systemReady, finishBooting, setLockScreenShown, goingToSleep, wakingUp, shutdown, + updateSleepIfNeededLocked + }; + // @formatter:on + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XActivityManagerService(Methods.inputDispatchingTimedOut)); + listHook.add(new XActivityManagerService(Methods.appNotResponding)); + listHook.add(new XActivityManagerService(Methods.systemReady)); + listHook.add(new XActivityManagerService(Methods.finishBooting)); + // setLockScreenShown appears to be not present in some 4.2.2 ROMs + listHook.add(new XActivityManagerService(Methods.setLockScreenShown)); + listHook.add(new XActivityManagerService(Methods.goingToSleep)); + listHook.add(new XActivityManagerService(Methods.wakingUp)); + listHook.add(new XActivityManagerService(Methods.shutdown)); + listHook.add(new XActivityManagerService(Methods.updateSleepIfNeededLocked)); + return listHook; + } + + public static void setSemaphore(Semaphore semaphore) { + mOndemandSemaphore = semaphore; + } + + public static boolean canOnDemand() { + return (mFinishedBooting && !mLockScreen && !mSleeping); + } + + public static boolean canWriteUsageData() { + return !mShutdown; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case inputDispatchingTimedOut: + // Delay foreground ANRs while on demand dialog open + if (mOndemandSemaphore != null && mOndemandSemaphore.availablePermits() < 1) { + Util.log(this, Log.WARN, "Foreground ANR uid=" + getUidANR(param)); + param.setResult(5 * 1000); // 5 seconds + } + break; + + case appNotResponding: + // Ignore background ANRs while on demand dialog open + if (mOndemandSemaphore != null && mOndemandSemaphore.availablePermits() < 1) { + Util.log(this, Log.WARN, "Background ANR uid=" + getUidANR(param)); + param.setResult(null); + } + break; + + case systemReady: + // Do nothing + break; + + case finishBooting: + // Do nothing + break; + + case setLockScreenShown: + if (param.args.length > 0 && param.args[0] instanceof Boolean) + try { + if ((Boolean) param.args[0]) { + mLockScreen = true; + Util.log(this, Log.WARN, "Lockscreen=" + mLockScreen); + } + } catch (Throwable ex) { + Util.bug(this, ex); + } + break; + + case goingToSleep: + mSleeping = true; + Util.log(this, Log.WARN, "Sleeping=" + mSleeping); + break; + + case wakingUp: + // Do nothing + break; + + case shutdown: + mShutdown = true; + Util.log(this, Log.WARN, "Shutdown"); + break; + + case updateSleepIfNeededLocked: + // Do nothing; + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case inputDispatchingTimedOut: + case appNotResponding: + break; + + case systemReady: + // Do nothing + Util.log(this, Log.WARN, "System ready"); + break; + + case finishBooting: + mFinishedBooting = true; + Util.log(this, Log.WARN, "Finished booting"); + break; + + case setLockScreenShown: + if (param.args.length > 0 && param.args[0] instanceof Boolean) + if (!(Boolean) param.args[0]) { + mLockScreen = false; + Util.log(this, Log.WARN, "Lockscreen=" + mLockScreen); + } + break; + + case goingToSleep: + // Do nothing + break; + + case wakingUp: + mSleeping = false; + Util.log(this, Log.WARN, "Sleeping=" + mSleeping); + break; + + case shutdown: + // Do nothing + break; + + case updateSleepIfNeededLocked: + if (param.thisObject != null) { + Field methodSleeping = param.thisObject.getClass().getDeclaredField("mSleeping"); + methodSleeping.setAccessible(true); + mSleeping = (Boolean) methodSleeping.get(param.thisObject); + Util.log(this, Log.WARN, "Sleeping=" + mSleeping); + } + break; + } + } + + // Helper methods + + private int getUidANR(XParam param) throws IllegalAccessException { + int uid = -1; + try { + Class pr = Class.forName("com.android.server.am.ProcessRecord"); + if (param.args.length > 0 && param.args[0] != null && param.args[0].getClass().equals(pr)) { + Field fUid = pr.getDeclaredField("uid"); + fUid.setAccessible(true); + uid = (Integer) fUid.get(param.args[0]); + } + } catch (ClassNotFoundException ignored) { + } catch (NoSuchFieldException ignored) { + } catch (Throwable ex) { + Util.bug(this, ex); + } + return uid; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityRecognitionApi.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityRecognitionApi.java new file mode 100644 index 0000000..bda47cc --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityRecognitionApi.java @@ -0,0 +1,65 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XActivityRecognitionApi extends XHook { + private Methods mMethod; + private String mClassName; + + private XActivityRecognitionApi(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), "GMS5." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // Location getLastLocation(GoogleApiClient client) + // abstract PendingResult removeActivityUpdates(GoogleApiClient client, PendingIntent callbackIntent) + // abstract PendingResult requestActivityUpdates(GoogleApiClient client, long detectionIntervalMillis, PendingIntent callbackIntent) + // https://developer.android.com/reference/com/google/android/gms/location/ActivityRecognitionApi.html + + // @formatter:on + + private enum Methods { + removeActivityUpdates, requestActivityUpdates + }; + + public static List getInstances(Object instance) { + String className = instance.getClass().getName(); + Util.log(null, Log.INFO, "Hooking ActivityRecognitionApi class=" + className + " uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XActivityRecognitionApi(Methods.removeActivityUpdates, null, className)); + listHook.add(new XActivityRecognitionApi(Methods.requestActivityUpdates, PrivacyManager.cLocation, className)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case removeActivityUpdates: + if (isRestricted(param, PrivacyManager.cLocation, "GMS5.requestActivityUpdates")) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + break; + + case requestActivityUpdates: + if (isRestricted(param)) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityRecognitionClient.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityRecognitionClient.java new file mode 100644 index 0000000..99e2bd1 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityRecognitionClient.java @@ -0,0 +1,56 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +public class XActivityRecognitionClient extends XHook { + private Methods mMethod; + + private XActivityRecognitionClient(Methods method, String restrictionName) { + super(restrictionName, method.name(), String.format("GMS.%s", method.name())); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.gms.location.ActivityRecognitionClient"; + } + + // @formatter:off + + // public void removeActivityUpdates(PendingIntent callbackIntent) + // public void requestActivityUpdates(long detectionIntervalMillis, PendingIntent callbackIntent) + // http://developer.android.com/reference/com/google/android/gms/location/ActivityRecognitionClient.html + + // @formatter:on + + private enum Methods { + removeActivityUpdates, requestActivityUpdates + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XActivityRecognitionClient(Methods.removeActivityUpdates, null)); + listHook.add(new XActivityRecognitionClient(Methods.requestActivityUpdates, PrivacyManager.cLocation)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case removeActivityUpdates: + if (isRestricted(param, PrivacyManager.cLocation, "GMS.requestActivityUpdates")) + param.setResult(null); + break; + + case requestActivityUpdates: + if (isRestricted(param)) + param.setResult(null); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityThread.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityThread.java new file mode 100644 index 0000000..fbcba3d --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XActivityThread.java @@ -0,0 +1,217 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.os.Binder; +import android.os.Message; +import android.provider.Telephony; +import android.service.notification.NotificationListenerService; +import android.telephony.TelephonyManager; +import android.util.Log; + +@SuppressLint("InlinedApi") +public class XActivityThread extends XHook { + private Methods mMethod; + private static Map mapActionRestriction = new HashMap(); + + static { + // Intent receive: calling + mapActionRestriction.put(Intent.ACTION_NEW_OUTGOING_CALL, PrivacyManager.cCalling); + mapActionRestriction.put(TelephonyManager.ACTION_PHONE_STATE_CHANGED, PrivacyManager.cPhone); + mapActionRestriction.put(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, PrivacyManager.cCalling); + + // Intent receive: C2DM + mapActionRestriction.put("com.google.android.c2dm.intent.REGISTRATION", PrivacyManager.cNotifications); + mapActionRestriction.put("com.google.android.c2dm.intent.RECEIVE", PrivacyManager.cNotifications); + + // Intent receive: NFC + mapActionRestriction.put(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED, PrivacyManager.cNfc); + mapActionRestriction.put(NfcAdapter.ACTION_NDEF_DISCOVERED, PrivacyManager.cNfc); + mapActionRestriction.put(NfcAdapter.ACTION_TAG_DISCOVERED, PrivacyManager.cNfc); + mapActionRestriction.put(NfcAdapter.ACTION_TECH_DISCOVERED, PrivacyManager.cNfc); + + // Intent receive: SMS + mapActionRestriction.put(Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION, PrivacyManager.cMessages); + mapActionRestriction.put(Telephony.Sms.Intents.SMS_RECEIVED_ACTION, PrivacyManager.cMessages); + mapActionRestriction.put(Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION, PrivacyManager.cMessages); + mapActionRestriction.put(Telephony.Sms.Intents.SMS_DELIVER_ACTION, PrivacyManager.cMessages); + mapActionRestriction.put(Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION, PrivacyManager.cMessages); + + // Intent receive: notifications + mapActionRestriction.put(NotificationListenerService.SERVICE_INTERFACE, PrivacyManager.cNotifications); + + // Intent receive: package changes + mapActionRestriction.put(Intent.ACTION_PACKAGE_ADDED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_REPLACED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_RESTARTED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_REMOVED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_CHANGED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_DATA_CLEARED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_FIRST_LAUNCH, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_FULLY_REMOVED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_PACKAGE_VERIFIED, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, PrivacyManager.cSystem); + mapActionRestriction.put(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE, PrivacyManager.cSystem); + } + + private XActivityThread(Methods method) { + super(null, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return (mMethod == Methods.handleReceiver ? "android.app.ActivityThread" : "android.os.MessageQueue"); + } + + @Override + public boolean isVisible() { + return false; + } + + private enum Methods { + next, handleReceiver + }; + + // @formatter:off + + // private void handleReceiver(ReceiverData data) + // frameworks/base/core/java/android/app/ActivityThread.java + + // final Message next() + // frameworks/base/core/java/android/android/os/MessageQueue.java + + // @formatter:on + + public static List getInstances() { + List listHook = new ArrayList(); + + listHook.add(new XActivityThread(Methods.next)); + listHook.add(new XActivityThread(Methods.handleReceiver)); + + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.next) { + // Do nothing + + } else if (mMethod == Methods.handleReceiver) { + if (param.args.length > 0 && param.args[0] != null) { + Field fieldIntent = param.args[0].getClass().getDeclaredField("intent"); + fieldIntent.setAccessible(true); + Intent intent = (Intent) fieldIntent.get(param.args[0]); + if (intent != null) { + if (checkIntent(Binder.getCallingUid(), intent)) { + finish(param); + param.setResult(null); + } + } + } + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.next) { + Message msg = (Message) param.getResult(); + if (msg != null) { + if (msg.obj instanceof Intent) { + Intent intent = (Intent) msg.obj; + if (intent != null) + if (checkIntent(Binder.getCallingUid(), intent)) + param.setResult(null); + } + } + + } else if (mMethod == Methods.handleReceiver) { + // Do nothing + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + private boolean checkIntent(int uid, Intent intent) throws Throwable { + String action = intent.getAction(); + if (mapActionRestriction.containsKey(action)) { + // Get restriction category + String restrictionName = mapActionRestriction.get(action); + + if (Intent.ACTION_NEW_OUTGOING_CALL.equals(action)) { + // Outgoing call + if (intent.hasExtra(Intent.EXTRA_PHONE_NUMBER)) { + String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); + if (phoneNumber != null) + if (isRestrictedExtraValue(uid, restrictionName, action, phoneNumber, phoneNumber)) + intent.putExtra(Intent.EXTRA_PHONE_NUMBER, + (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "PhoneNumber")); + } + + } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { + // Incoming call + if (intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)) { + String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); + if (phoneNumber != null) { + if (isRestrictedExtraValue(uid, restrictionName, action, phoneNumber, phoneNumber)) + intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, + (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "PhoneNumber")); + } + } + + } else if (PrivacyManager.cSystem.equals(restrictionName)) { + // Package event + if (isRestrictedExtra(uid, restrictionName, action, intent.getDataString())) { + String[] packageNames; + if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE) + || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) + packageNames = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + else + packageNames = new String[] { intent.getData().getSchemeSpecificPart() }; + for (String packageName : packageNames) + if (!XPackageManager.isPackageAllowed(0, packageName)) + return true; + } + + } else if (isRestrictedExtra(uid, restrictionName, action, intent.getDataString())) + return true; + + } + + return false; + } + + private void finish(XParam param) { + // unscheduleGcIdler + if (param.thisObject != null) + try { + Method unschedule = param.thisObject.getClass().getDeclaredMethod("unscheduleGcIdler"); + unschedule.setAccessible(true); + unschedule.invoke(param.thisObject); + } catch (Throwable ex) { + Util.bug(this, ex); + } + + // data.finish + if (param.args[0] instanceof BroadcastReceiver.PendingResult) + try { + BroadcastReceiver.PendingResult pr = (BroadcastReceiver.PendingResult) param.args[0]; + pr.finish(); + } catch (IllegalStateException ignored) { + // No receivers for action ... + } catch (Throwable ex) { + Util.bug(this, ex); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAdvertisingIdClientInfo.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAdvertisingIdClientInfo.java new file mode 100644 index 0000000..6113c5c --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAdvertisingIdClientInfo.java @@ -0,0 +1,54 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XAdvertisingIdClientInfo extends XHook { + private Methods mMethod; + + private XAdvertisingIdClientInfo(Methods method, String restrictionName, String specifier) { + super(restrictionName, method.name(), specifier); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.gms.ads.identifier.AdvertisingIdClient$Info"; + } + + // @formatter:off + + // String getId() + // http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info.html + + // @formatter:on + + private enum Methods { + getId + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XAdvertisingIdClientInfo(Methods.getId, PrivacyManager.cIdentification, "AdvertisingId")); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.getId) { + String adid = (String) param.getResult(); + if (adid != null) + if (isRestrictedValue(param, adid)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "AdvertisingId")); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAppIndexApi.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAppIndexApi.java new file mode 100644 index 0000000..24460dd --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAppIndexApi.java @@ -0,0 +1,66 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XAppIndexApi extends XHook { + private Methods mMethod; + private String mClassName; + + private XAppIndexApi(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), "GMS5." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // abstract PendingResult view(GoogleApiClient apiClient, Activity activity, Intent viewIntent, String title, Uri webUrl, List outLinks) + // abstract PendingResult view(GoogleApiClient apiClient, Activity activity, Uri appIndexingUrl, String title, Uri webUrl, List outLinks) + // abstract PendingResult viewEnd(GoogleApiClient apiClient, Activity activity, Uri appIndexingUrl) + // abstract PendingResult viewEnd(GoogleApiClient apiClient, Activity activity, Intent viewIntent) + // https://developer.android.com/reference/com/google/android/gms/appindexing/AppIndexApi.html + + // @formatter:on + + private enum Methods { + view, viewEnd + }; + + public static List getInstances(Object instance) { + String className = instance.getClass().getName(); + Util.log(null, Log.INFO, "Hooking AppIndex class=" + className + " uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XAppIndexApi(Methods.viewEnd, null, className)); + listHook.add(new XAppIndexApi(Methods.view, PrivacyManager.cView, className)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case viewEnd: + if (isRestricted(param, PrivacyManager.cView, "GMS5.view")) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + break; + + case view: + if (isRestricted(param)) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAppWidgetManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAppWidgetManager.java new file mode 100644 index 0000000..b1b6b8b --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAppWidgetManager.java @@ -0,0 +1,75 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.appwidget.AppWidgetProviderInfo; +import android.os.Build; + +public class XAppWidgetManager extends XHook { + private Methods mMethod; + private String mClassName; + + private XAppWidgetManager(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + if (method.name().startsWith("Srv_")) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + mClassName = "com.android.server.appwidget.AppWidgetServiceImpl"; + else + mClassName = "com.android.server.AppWidgetService"; + else + mClassName = "android.appwidget.AppWidgetManager"; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public List getInstalledProviders() + // public List getInstalledProvidersForProfile(UserHandle profile) + // frameworks/base/core/java/android/appwidget/AppWidgetManager.java + // http://developer.android.com/reference/android/appwidget/AppWidgetManager.html + + // public List getInstalledProviders(int categoryFilter, int userId) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/com/android/server/AppWidgetService.java + + // @formatter:on + + private enum Methods { + getInstalledProviders, getInstalledProvidersForProfile, Srv_getInstalledProviders, Srv_getInstalledProvidersForProfile + }; + + public static List getInstances(boolean server) { + List listHook = new ArrayList(); + if (server) { + listHook.add(new XAppWidgetManager(Methods.Srv_getInstalledProviders, PrivacyManager.cSystem)); + listHook.add(new XAppWidgetManager(Methods.Srv_getInstalledProvidersForProfile, PrivacyManager.cSystem)); + } else { + listHook.add(new XAppWidgetManager(Methods.getInstalledProviders, PrivacyManager.cSystem)); + listHook.add(new XAppWidgetManager(Methods.getInstalledProvidersForProfile, PrivacyManager.cSystem)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getInstalledProviders: + case getInstalledProvidersForProfile: + case Srv_getInstalledProviders: + case Srv_getInstalledProvidersForProfile: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XApplication.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XApplication.java new file mode 100644 index 0000000..f5c2214 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XApplication.java @@ -0,0 +1,125 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Process; +import android.util.Log; + +public class XApplication extends XHook { + private Methods mMethod; + + private static boolean mReceiverInstalled = false; + + public static String cAction = "Action"; + public static String cActionKillProcess = "Kill"; + public static String cActionFlush = "Flush"; + + public static String ACTION_MANAGE_PACKAGE = "biz.bokhorst.xprivacy.ACTION_MANAGE_PACKAGE"; + public static String PERMISSION_MANAGE_PACKAGES = "biz.bokhorst.xprivacy.MANAGE_PACKAGES"; + + public XApplication(Methods method, String restrictionName, String actionName) { + super(restrictionName, method.name(), actionName); + mMethod = method; + } + + @Override + public String getClassName() { + return "android.app.Application"; + } + + // public void onCreate() + // frameworks/base/core/java/android/app/Application.java + // http://developer.android.com/reference/android/app/Application.html + + private enum Methods { + onCreate + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XApplication(Methods.onCreate, null, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case onCreate: + // Install receiver for package management + if (PrivacyManager.isApplication(Process.myUid()) && !mReceiverInstalled) + try { + Application app = (Application) param.thisObject; + if (app != null) { + mReceiverInstalled = true; + Util.log(this, Log.INFO, "Installing receiver uid=" + Process.myUid()); + app.registerReceiver(new Receiver(app), new IntentFilter(ACTION_MANAGE_PACKAGE), + PERMISSION_MANAGE_PACKAGES, null); + } + } catch (SecurityException ignored) { + } catch (Throwable ex) { + Util.bug(this, ex); + } + break; + } + } + + public static void manage(Context context, int uid, String action) { + if (uid == 0) + manage(context, null, action); + else { + String[] packageName = context.getPackageManager().getPackagesForUid(uid); + if (packageName != null && packageName.length > 0) + manage(context, packageName[0], action); + else + Util.log(null, Log.WARN, "No packages uid=" + uid + " action=" + action); + } + } + + public static void manage(Context context, String packageName, String action) { + Util.log(null, Log.INFO, "Manage package=" + packageName + " action=" + action); + + if (packageName == null && XApplication.cActionKillProcess.equals(action)) { + Util.log(null, Log.WARN, "Kill all"); + return; + } + + Intent manageIntent = new Intent(XApplication.ACTION_MANAGE_PACKAGE); + manageIntent.putExtra(XApplication.cAction, action); + if (packageName != null) + manageIntent.setPackage(packageName); + context.sendBroadcast(manageIntent); + } + + private class Receiver extends BroadcastReceiver { + public Receiver(Application app) { + } + + @Override + public void onReceive(Context context, Intent intent) { + try { + String action = intent.getExtras().getString(cAction); + Util.log(null, Log.WARN, "Managing uid=" + Process.myUid() + " action=" + action); + + if (cActionKillProcess.equals(action)) + android.os.Process.killProcess(Process.myPid()); + else if (cActionFlush.equals(action)) { + PrivacyManager.flush(); + } else + Util.log(null, Log.WARN, "Unknown management action=" + action); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAudioRecord.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAudioRecord.java new file mode 100644 index 0000000..8714936 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XAudioRecord.java @@ -0,0 +1,54 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +public class XAudioRecord extends XHook { + private Methods mMethod; + + private XAudioRecord(Methods method, String restrictionName) { + super(restrictionName, method.name(), "Audio." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.media.AudioRecord"; + } + + // public void startRecording() + // public void startRecording(MediaSyncEvent syncEvent) + // public void stop() + // frameworks/base/media/java/android/media/AudioRecord.java + // http://developer.android.com/reference/android/media/AudioRecord.html + + private enum Methods { + startRecording, stop + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XAudioRecord(Methods.startRecording, PrivacyManager.cMedia)); + listHook.add(new XAudioRecord(Methods.stop, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case startRecording: + if (isRestricted(param)) + param.setResult(null); + break; + + case stop: + if (isRestricted(param, PrivacyManager.cMedia, "Audio.startRecording")) + param.setResult(null); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBinder.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBinder.java new file mode 100644 index 0000000..62b7999 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBinder.java @@ -0,0 +1,337 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.content.Context; +import android.os.Binder; +import android.os.Build; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Process; +import android.util.Log; +import android.util.SparseArray; + +public class XBinder extends XHook { + private Methods mMethod; + private static long mToken = 0; + private static Map mMapClassSystem = new HashMap(); + private static Map> mMapCodeName = new HashMap>(); + + private static final int BITS_TOKEN = 16; + private static final int FLAG_ALL = 0xFFFF; + private static final int MASK_TOKEN = 0xFFFF; + + private static final int PING_TRANSACTION = ('_' << 24) | ('P' << 16) | ('N' << 8) | 'G'; + private static final int DUMP_TRANSACTION = ('_' << 24) | ('D' << 16) | ('M' << 8) | 'P'; + private static final int INTERFACE_TRANSACTION = ('_' << 24) | ('N' << 16) | ('T' << 8) | 'F'; + private static final int TWEET_TRANSACTION = ('_' << 24) | ('T' << 16) | ('W' << 8) | 'T'; + private static final int LIKE_TRANSACTION = ('_' << 24) | ('L' << 16) | ('I' << 8) | 'K'; + private static final int SYSPROPS_TRANSACTION = ('_' << 24) | ('S' << 16) | ('P' << 8) | 'R'; + + // Service name should one-to-one correspond to the other lists + + // @formatter:off + public static List cServiceName = Arrays.asList(new String[] { + "account", + "activity", + "clipboard", + "connectivity", + "content", + "location", + "telephony.registry", + "telephony.msim.registry", + "package", + "iphonesubinfo", + "iphonesubinfo_msim", + "window", + "wifi", + "sip", + "isms", + "nfc", + "appwidget", + "bluetooth", + "bluetooth_manager", + "input", + "sensorservice", + "usb", + "media.camera", + "", + "", + "" + }); + // @formatter:on + + // @formatter:off + public static List cServiceDescriptor = Arrays.asList(new String[] { + "android.accounts.IAccountManager", + "android.app.IActivityManager", + "android.content.IClipboard", + "android.net.IConnectivityManager", + "android.content.IContentService", + "android.location.ILocationManager", + "com.android.internal.telephony.ITelephonyRegistry", + "com.android.internal.telephony.ITelephonyRegistryMSim", + "android.content.pm.IPackageManager", + "com.android.internal.telephony.IPhoneSubInfo", + "com.android.internal.telephony.msim.IPhoneSubInfoMSim", + "android.view.IWindowManager", + "android.net.wifi.IWifiManager", + "android.net.sip.ISipService", + "com.android.internal.telephony.ISms", + "android.nfc.INfcAdapter", + "com.android.internal.appwidget.IAppWidgetService", + "android.bluetooth.IBluetooth", + "android.bluetooth.IBluetoothManager", + "android.hardware.input.IInputManager", + "android.gui.SensorServer", + "android.hardware.usb.IUsbManager", + "android.hardware.ICameraService", + "android.app.IApplicationThread", + "android.content.IContentProvider", + "android.view.IWindowSession" + }); + // @formatter:on + + // @formatter:off + public static List cServiceOptional = Arrays.asList(new String[] { + "", + "iphonesubinfo", + "iphonesubinfo_msim", + "sip", + "isms", + "nfc", + "bluetooth", + "bluetooth_manager" + }); + // @formatter:on + + private XBinder(Methods method, String restrictionName) { + super(restrictionName, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return (mMethod == Methods.transact ? "android.os.BinderProxy" : "android.os.Binder"); + } + + public boolean isVisible() { + return (mMethod != Methods.execTransact); + } + + @Override + public void setSecret(String secret) { + super.setSecret(secret); + mToken = (secret.hashCode() & MASK_TOKEN); + } + + // @formatter:off + + // private boolean execTransact(int code, int dataObj, int replyObj, int flags) + // public final boolean transact(int code, Parcel data, Parcel reply, int flags) + // public native boolean transact(int code, Parcel data, Parcel reply, int flags) + // frameworks/base/core/java/android/os/Binder.java + // http://developer.android.com/reference/android/os/Binder.html + + // @formatter:on + + private enum Methods { + execTransact, transact + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XBinder(Methods.execTransact, null)); // Binder + listHook.add(new XBinder(Methods.transact, null)); // BinderProxy + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.execTransact) { + // execTransact calls the overridden onTransact + + // Check for direct IPC + checkIPC(param); + + } else if (mMethod == Methods.transact) { + markIPC(param); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } + + private void markIPC(XParam param) throws Throwable { + // Allow management transactions + int code = (Integer) param.args[0]; + if (isManagementTransaction(code)) + return; + + // Only for applications + int uid = Binder.getCallingUid(); + if (!PrivacyManager.isApplication(uid)) + return; + + // Check interface name + IBinder binder = (IBinder) param.thisObject; + String descriptor = (binder == null ? null : binder.getInterfaceDescriptor()); + if (!cServiceDescriptor.contains(descriptor)) + return; + + // Search this object in call stack + boolean ok = false; + boolean found = false; + StackTraceElement[] ste = Thread.currentThread().getStackTrace(); + for (int i = 0; i < ste.length; i++) + if (ste[i].getClassName().equals(param.thisObject.getClass().getName())) { + found = true; + + // Check if caller class in user space + String callerClassName = (i + 2 < ste.length ? ste[i + 2].getClassName() : null); + if (callerClassName != null && !callerClassName.startsWith("java.lang.reflect.")) + synchronized (mMapClassSystem) { + if (!mMapClassSystem.containsKey(callerClassName)) + try { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class clazz = Class.forName(callerClassName, false, loader); + boolean boot = Context.class.getClassLoader().equals(clazz.getClassLoader()); + mMapClassSystem.put(callerClassName, boot); + } catch (ClassNotFoundException ignored) { + mMapClassSystem.put(callerClassName, true); + } + ok = mMapClassSystem.get(callerClassName); + } + + break; + } + + // Conditionally mark + if (ok) { + int flags = (Integer) param.args[3]; + if ((flags & ~FLAG_ALL) != 0) + Util.log(this, Log.ERROR, "Unknown flags=" + Integer.toHexString(flags) + " descriptor=" + descriptor + + " code=" + code + " uid=" + Binder.getCallingUid()); + flags |= (mToken << BITS_TOKEN); + param.args[3] = flags; + } else { + int level = (found ? Log.WARN : Log.ERROR); + Util.log(this, level, "Unmarked descriptor=" + descriptor + " found=" + found + " code=" + code + " uid=" + + Binder.getCallingUid()); + Util.logStack(this, level, true); + } + } + + // Entry point from android_util_Binder.cpp's onTransact + private void checkIPC(XParam param) throws Throwable { + // Allow management transactions + int code = (Integer) param.args[0]; + if (isManagementTransaction(code)) + return; + + // Only for applications + int uid = Binder.getCallingUid(); + if (!PrivacyManager.isApplication(uid)) + return; + + // Check interface name + IBinder binder = (IBinder) param.thisObject; + String descriptor = (binder == null ? null : binder.getInterfaceDescriptor()); + if (!cServiceDescriptor.contains(descriptor)) + return; + + // Get token + int flags = (Integer) param.args[3]; + long token = (flags >> BITS_TOKEN) & MASK_TOKEN; + flags &= FLAG_ALL; + param.args[3] = flags; + + // Check token + if (token != mToken) { + String[] name = descriptor.split("\\."); + String interfaceName = name[name.length - 1]; + + // Get transaction code name + String codeName; + synchronized (mMapCodeName) { + if (!mMapCodeName.containsKey(descriptor)) { + SparseArray sa = new SparseArray(); + mMapCodeName.put(descriptor, sa); + + List> listClass = new ArrayList>(); + if (param.thisObject.getClass().getSuperclass() != null) + listClass.add(param.thisObject.getClass().getSuperclass()); + try { + listClass.add(Class.forName(descriptor)); + } catch (ClassNotFoundException ignored) { + } + + for (Class clazz : listClass) + for (Field field : clazz.getDeclaredFields()) + try { + if (field.getName().startsWith("TRANSACTION_") + || field.getName().endsWith("_TRANSACTION")) { + field.setAccessible(true); + Integer txCode = (Integer) field.get(null); + String txName = field.getName().replace("TRANSACTION_", "") + .replace("_TRANSACTION", ""); + sa.put(txCode, txName); + } + } catch (Throwable ignore) { + } + } + + codeName = mMapCodeName.get(descriptor).get(code); + } + if (codeName == null) { + codeName = Integer.toString(code); + Util.log(this, Log.WARN, "Unknown transaction=" + descriptor + ":" + code + " class=" + + param.thisObject.getClass() + " uid=" + Binder.getCallingUid()); + Util.logStack(this, Log.INFO); + } + + Util.log(this, Log.INFO, "can restrict transaction=" + interfaceName + ":" + codeName + " flags=" + flags + + " uid=" + uid + " my=" + Process.myUid()); + + if (isRestrictedExtra(uid, PrivacyManager.cIPC, "Binder", interfaceName + ":" + codeName)) { + Util.log(this, Log.WARN, "Restricting " + interfaceName + ":" + codeName + " code=" + code); + // Get reply parcel + Parcel reply = null; + try { + // static protected final Parcel obtain(int obj) + // frameworks/base/core/java/android/os/Parcel.java + Method methodObtain = Parcel.class.getDeclaredMethod("obtain", + Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? int.class : long.class); + methodObtain.setAccessible(true); + reply = (Parcel) methodObtain.invoke(null, param.args[2]); + } catch (NoSuchMethodException ex) { + Util.bug(this, ex); + } + + // Block IPC + if (reply == null) + Util.log(this, Log.ERROR, "reply is null uid=" + uid); + else { + reply.setDataPosition(0); + reply.writeException(new SecurityException("XPrivacy")); + } + param.setResult(true); + } + } + } + + private static boolean isManagementTransaction(int code) { + return (code == PING_TRANSACTION || code == DUMP_TRANSACTION || code == INTERFACE_TRANSACTION + || code == TWEET_TRANSACTION || code == LIKE_TRANSACTION || code == SYSPROPS_TRANSACTION); + + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBluetoothAdapter.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBluetoothAdapter.java new file mode 100644 index 0000000..870b2b0 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBluetoothAdapter.java @@ -0,0 +1,84 @@ +package biz.bokhorst.xprivacy; + +import biz.bokhorst.xprivacy.XHook; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import android.bluetooth.BluetoothDevice; +import android.os.Binder; + +public class XBluetoothAdapter extends XHook { + private Methods mMethod; + private String mClassName; + + private XBluetoothAdapter(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), "Bluetooth." + method.name()); + mMethod = method; + if (method.name().startsWith("Srv_")) + mClassName = "com.android.server.BluetoothManagerService"; + else + mClassName = "android.bluetooth.BluetoothAdapter"; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public String getAddress() + // public Set getBondedDevices() + // public String getName() + // frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java + // http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/com/android/server/BluetoothManagerService.java + + // @formatter:on + + private enum Methods { + getAddress, getBondedDevices, Srv_getAddress, Srv_getName + }; + + public static List getInstances(boolean server) { + List listHook = new ArrayList(); + if (server) { + listHook.add(new XBluetoothAdapter(Methods.Srv_getAddress, PrivacyManager.cNetwork)); + listHook.add(new XBluetoothAdapter(Methods.Srv_getName, PrivacyManager.cNetwork)); + } else { + listHook.add(new XBluetoothAdapter(Methods.getAddress, PrivacyManager.cNetwork)); + listHook.add(new XBluetoothAdapter(Methods.getBondedDevices, PrivacyManager.cNetwork)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + int uid = Binder.getCallingUid(); + switch (mMethod) { + case getAddress: + case Srv_getAddress: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(uid, "MAC")); + break; + + case Srv_getName: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(uid, "BTName")); + break; + + case getBondedDevices: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new HashSet()); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBluetoothDevice.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBluetoothDevice.java new file mode 100644 index 0000000..e7d9490 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XBluetoothDevice.java @@ -0,0 +1,50 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; +import biz.bokhorst.xprivacy.XHook; + +public class XBluetoothDevice extends XHook { + private Methods mMethod; + + private XBluetoothDevice(Methods method, String restrictionName) { + super(restrictionName, method.name(), "Bluetooth." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.bluetooth.BluetoothDevice"; + } + + // public String getAddress() + // frameworks/base/core/java/android/bluetooth/BluetoothDevice.java + // http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html + + private enum Methods { + getAddress + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XBluetoothDevice(Methods.getAddress, PrivacyManager.cNetwork)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.getAddress) { + if (param.getResult() != null && isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "MAC")); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCamera.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCamera.java new file mode 100644 index 0000000..68d5bcd --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCamera.java @@ -0,0 +1,70 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +public class XCamera extends XHook { + private Methods mMethod; + + private XCamera(Methods method, String restrictionName) { + super(restrictionName, method.name(), "Camera." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.hardware.Camera"; + } + + // @formatter:off + + // public void setPreviewCallback(Camera.PreviewCallback cb) + // public void setPreviewCallbackWithBuffer(Camera.PreviewCallback cb) + // public void setPreviewDisplay(SurfaceHolder holder) + // public void setPreviewTexture(SurfaceTexture surfaceTexture) + // public final void setOneShotPreviewCallback (Camera.PreviewCallback cb) + // public native final void startPreview() + // public void stopPreview() + // public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg) + // public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback postview, PictureCallback jpeg) + // frameworks/base/core/java/android/hardware/Camera.java + // http://developer.android.com/reference/android/hardware/Camera.html + + // @formatter:on + + private enum Methods { + setPreviewCallback, setPreviewCallbackWithBuffer, setPreviewDisplay, setPreviewTexture, setOneShotPreviewCallback, startPreview, stopPreview, takePicture + }; + + public static List getInstances() { + List listHook = new ArrayList(); + for (Methods cam : Methods.values()) + listHook.add(new XCamera(cam, cam == Methods.stopPreview ? null : PrivacyManager.cMedia)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case setPreviewCallback: + case setPreviewCallbackWithBuffer: + case setPreviewDisplay: + case setPreviewTexture: + case setOneShotPreviewCallback: + case startPreview: + case takePicture: + if (isRestricted(param)) + param.setResult(null); + break; + + case stopPreview: + if (isRestricted(param, PrivacyManager.cMedia, "Camera.startPreview")) + param.setResult(null); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCameraDevice2.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCameraDevice2.java new file mode 100644 index 0000000..0f1f7ca --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCameraDevice2.java @@ -0,0 +1,56 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +public class XCameraDevice2 extends XHook { + private Methods mMethod; + + private XCameraDevice2(Methods method, String restrictionName) { + super(restrictionName, method.name(), "Camera2." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.hardware.camera2.impl.CameraDeviceImpl"; + } + + // @formatter:off + + // public int capture(CaptureRequest request, CaptureListener listener, Handler handler) + // public int captureBurst(List requests, CaptureListener listener, Handler handler) + // public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, Handler handler) + // public int setRepeatingBurst(List requests, CaptureListener listener, Handler handler) + // frameworks/base/core/java/android/hardware/camera2/impl/CameraDevice.java + // http://developer.android.com/reference/android/hardware/camera2/CameraDevice.html + + // @formatter:on + + private enum Methods { + capture, captureBurst, setRepeatingRequest, setRepeatingBurst + }; + + public static List getInstances() { + List listHook = new ArrayList(); + for (Methods cam : Methods.values()) + listHook.add(new XCameraDevice2(cam, PrivacyManager.cMedia)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.capture || mMethod == Methods.captureBurst) { + if (isRestricted(param)) + param.setResult(0); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCastDevice.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCastDevice.java new file mode 100644 index 0000000..7c1723d --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XCastDevice.java @@ -0,0 +1,56 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import biz.bokhorst.xprivacy.XHook; + +public class XCastDevice extends XHook { + private Methods mMethod; + + private XCastDevice(Methods method, String restrictionName) { + super(restrictionName, method.name(), "Cast." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.gms.cast.CastDevice"; + } + + // public static getDeviceId() + // public Inet4Address getIpAddress() + // http://developer.android.com/reference/com/google/android/gms/cast/CastDevice.html + + private enum Methods { + getDeviceId, getIpAddress + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XCastDevice(Methods.getDeviceId, PrivacyManager.cIdentification)); + listHook.add(new XCastDevice(Methods.getIpAddress, PrivacyManager.cIdentification)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + switch (mMethod) { + case getDeviceId: + if (param.getResult() != null && isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "CastID")); + break; + + case getIpAddress: + if (param.getResult() != null && isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "InetAddress")); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XClipboardManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XClipboardManager.java new file mode 100644 index 0000000..d5d2ffe --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XClipboardManager.java @@ -0,0 +1,152 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Build; + +public class XClipboardManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.content.ClipboardManager"; + + private XClipboardManager(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + mClassName = "com.android.server.clipboard.ClipboardService"; + else + mClassName = "com.android.server.ClipboardService"; + } + + private XClipboardManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) + // public ClipData getPrimaryClip() + // public ClipDescription getPrimaryClipDescription() + // public CharSequence getText() + // public boolean hasPrimaryClip() + // public boolean hasText() + // public void removePrimaryClipChangedListener(ClipboardManager.OnPrimaryClipChangedListener what) + // frameworks/base/core/java/android/content/ClipboardManager.java + // http://developer.android.com/reference/android/content/ClipboardManager.html + + // @formatter:on + + // @formatter:off + private enum Methods { + addPrimaryClipChangedListener, + getPrimaryClip, + getPrimaryClipDescription, + getText, + hasPrimaryClip, + hasText, + removePrimaryClipChangedListener, + + Srv_addPrimaryClipChangedListener, + Srv_getPrimaryClip, + Srv_getPrimaryClipDescription, + Srv_hasClipboardText, + Srv_hasPrimaryClip, + Srv_removePrimaryClipChangedListener + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + for (Methods clip : Methods.values()) + if (clip.name().startsWith("Srv_")) { + if (server) + if (clip == Methods.Srv_removePrimaryClipChangedListener) + listHook.add(new XClipboardManager(clip, null)); + else + listHook.add(new XClipboardManager(clip, PrivacyManager.cClipboard)); + } else if (!server) { + if (clip == Methods.removePrimaryClipChangedListener) + listHook.add(new XClipboardManager(clip, null, className)); + else + listHook.add(new XClipboardManager(clip, PrivacyManager.cClipboard, className)); + } + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case addPrimaryClipChangedListener: + case Srv_addPrimaryClipChangedListener: + if (isRestricted(param)) + param.setResult(null); + break; + + case getPrimaryClip: + case getPrimaryClipDescription: + case getText: + case hasPrimaryClip: + case hasText: + break; + + case removePrimaryClipChangedListener: + if (isRestricted(param, PrivacyManager.cClipboard, "addPrimaryClipChangedListener")) + param.setResult(null); + break; + + case Srv_removePrimaryClipChangedListener: + if (isRestricted(param, PrivacyManager.cClipboard, "Srv_addPrimaryClipChangedListener")) + param.setResult(null); + break; + + case Srv_getPrimaryClip: + case Srv_getPrimaryClipDescription: + case Srv_hasClipboardText: + case Srv_hasPrimaryClip: + break; + } + + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case addPrimaryClipChangedListener: + case removePrimaryClipChangedListener: + case Srv_addPrimaryClipChangedListener: + case Srv_removePrimaryClipChangedListener: + break; + + case getPrimaryClip: + case getPrimaryClipDescription: + case getText: + case Srv_getPrimaryClip: + case Srv_getPrimaryClipDescription: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(null); + break; + + case hasPrimaryClip: + case hasText: + case Srv_hasClipboardText: + case Srv_hasPrimaryClip: + if (param.getResult() instanceof Boolean) + if (isRestricted(param)) + param.setResult(false); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XConnectionCallbacks.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XConnectionCallbacks.java new file mode 100644 index 0000000..7b16491 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XConnectionCallbacks.java @@ -0,0 +1,131 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XConnectionCallbacks extends XHook { + private Methods mMethod; + private String mClassName; + + private XConnectionCallbacks(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), "GMS5." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // abstract void onConnected(Bundle connectionHint) + // https://developer.android.com/reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html + + // @formatter:on + + private enum Methods { + onConnected + }; + + public static List getInstances(Object instance) { + String className = instance.getClass().getName(); + Util.log(null, Log.INFO, "Hooking class=" + className + " uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XConnectionCallbacks(Methods.onConnected, null, className)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case onConnected: + Util.log(this, Log.WARN, "GoogleApiClient onConnected uid=" + Binder.getCallingUid()); + ClassLoader loader = param.thisObject.getClass().getClassLoader(); + + // FusedLocationApi + try { + Class cLoc = Class.forName("com.google.android.gms.location.LocationServices", false, loader); + Object fusedLocationApi = cLoc.getDeclaredField("FusedLocationApi").get(null); + if (PrivacyManager.getTransient(fusedLocationApi.getClass().getName(), null) == null) { + PrivacyManager.setTransient(fusedLocationApi.getClass().getName(), Boolean.toString(true)); + + if (fusedLocationApi != null) + XPrivacy.hookAll(XFusedLocationApi.getInstances(fusedLocationApi), loader, getSecret(), true); + } + } catch (ClassNotFoundException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (NoSuchFieldException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (ExceptionInInitializerError ex) { + Util.log(this, Log.WARN, ex.toString()); + } + + // ActivityRecognitionApi + try { + Class cRec = Class.forName("com.google.android.gms.location.ActivityRecognition", false, loader); + Object activityRecognitionApi = cRec.getDeclaredField("ActivityRecognitionApi").get(null); + if (PrivacyManager.getTransient(activityRecognitionApi.getClass().getName(), null) == null) { + PrivacyManager.setTransient(activityRecognitionApi.getClass().getName(), Boolean.toString(true)); + + if (activityRecognitionApi != null) + XPrivacy.hookAll(XActivityRecognitionApi.getInstances(activityRecognitionApi), loader, + getSecret(), true); + } + } catch (ClassNotFoundException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (NoSuchFieldException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (ExceptionInInitializerError ex) { + Util.log(this, Log.WARN, ex.toString()); + } + + // AppIndexApi + try { + Class cApp = Class.forName("com.google.android.gms.appindexing.AppIndex", false, loader); + Object appIndexApi = cApp.getDeclaredField("AppIndexApi").get(null); + if (PrivacyManager.getTransient(appIndexApi.getClass().getName(), null) == null) { + PrivacyManager.setTransient(appIndexApi.getClass().getName(), Boolean.toString(true)); + + if (appIndexApi != null) + XPrivacy.hookAll(XAppIndexApi.getInstances(appIndexApi), loader, getSecret(), true); + } + } catch (ClassNotFoundException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (NoSuchFieldException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (ExceptionInInitializerError ex) { + Util.log(this, Log.WARN, ex.toString()); + } + + // PlaceDetectionApi + try { + Class cPlaces = Class.forName("com.google.android.gms.location.places.Places", false, loader); + Object placeDetectionApi = cPlaces.getDeclaredField("PlaceDetectionApi").get(null); + if (PrivacyManager.getTransient(placeDetectionApi.getClass().getName(), null) == null) { + PrivacyManager.setTransient(placeDetectionApi.getClass().getName(), Boolean.toString(true)); + + if (placeDetectionApi != null) + XPrivacy.hookAll(XPlaceDetectionApi.getInstances(placeDetectionApi), loader, getSecret(), true); + } + } catch (ClassNotFoundException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (NoSuchFieldException ex) { + Util.log(this, Log.WARN, ex.toString()); + } catch (ExceptionInInitializerError ex) { + Util.log(this, Log.WARN, ex.toString()); + } + + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XConnectivityManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XConnectivityManager.java new file mode 100644 index 0000000..e4f5cfd --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XConnectivityManager.java @@ -0,0 +1,65 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.net.NetworkInfo; + +public class XConnectivityManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.net.ConnectivityManager"; + + private XConnectivityManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), "Connectivity." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // public NetworkInfo getActiveNetworkInfo() + // public NetworkInfo[] getAllNetworkInfo() + // public NetworkInfo getNetworkInfo(int networkType) + // frameworks/base/core/java/android/net/ConnectivityManager.java + // http://developer.android.com/reference/android/net/ConnectivityManager.html + + private enum Methods { + getActiveNetworkInfo, getAllNetworkInfo, getNetworkInfo + }; + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + for (Methods connmgr : Methods.values()) + listHook.add(new XConnectivityManager(connmgr, PrivacyManager.cInternet, className)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getActiveNetworkInfo: + case getNetworkInfo: + if (param.getResult() != null && isRestricted(param)) + param.setResult(null); + break; + + case getAllNetworkInfo: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new NetworkInfo[0]); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XContentResolver.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XContentResolver.java new file mode 100644 index 0000000..6d3626f --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XContentResolver.java @@ -0,0 +1,697 @@ +package biz.bokhorst.xprivacy; + +import java.io.FileNotFoundException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import android.annotation.SuppressLint; +import android.content.SyncAdapterType; +import android.content.SyncInfo; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +public class XContentResolver extends XHook { + private Methods mMethod; + private boolean mClient; + private String mClassName; + + private XContentResolver(Methods method, String restrictionName, String className) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + if (className == null) + mClassName = "com.android.server.content.ContentService"; + else + mClassName = className; + } + + private XContentResolver(Methods method, String restrictionName, boolean client) { + super(restrictionName, method.name(), null); + mMethod = method; + mClient = client; + mClassName = null; + } + + public String getClassName() { + if (mClassName == null) + return (mClient ? "android.content.ContentProviderClient" : "android.content.ContentResolver"); + else + return mClassName; + } + + // @formatter:off + + // public static SyncInfo getCurrentSync() + // static List getCurrentSyncs() + // static SyncAdapterType[] getSyncAdapterTypes() + + // final AssetFileDescriptor openAssetFileDescriptor(Uri uri, String mode) + // final AssetFileDescriptor openAssetFileDescriptor(Uri uri, String mode, CancellationSignal cancellationSignal) + // final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode, CancellationSignal cancellationSignal) + // final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode) + // final InputStream openInputStream(Uri uri) + // final OutputStream openOutputStream(Uri uri) + // final OutputStream openOutputStream(Uri uri, String mode) + // final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, String mimeType, Bundle opts, CancellationSignal cancellationSignal) + // final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, String mimeType, Bundle opts) + + // AssetFileDescriptor openAssetFile(Uri url, String mode, CancellationSignal signal) + // AssetFileDescriptor openAssetFile(Uri url, String mode) + // ParcelFileDescriptor openFile(Uri url, String mode) + // ParcelFileDescriptor openFile(Uri url, String mode, CancellationSignal signal) + + // public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) + // public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) + + // https://developers.google.com/gmail/android/ + // http://developer.android.com/reference/android/content/ContentResolver.html + // http://developer.android.com/reference/android/content/ContentProviderClient.html + + // http://developer.android.com/reference/android/provider/Contacts.People.html + // http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html + // http://developer.android.com/reference/android/provider/ContactsContract.Data.html + // http://developer.android.com/reference/android/provider/ContactsContract.PhoneLookup.html + // http://developer.android.com/reference/android/provider/ContactsContract.Profile.html + // http://developer.android.com/reference/android/provider/ContactsContract.RawContacts.html + + // frameworks/base/core/java/android/content/ContentResolver.java + + // public List getCurrentSyncs() + // public void registerContentObserver(android.net.Uri uri, boolean notifyForDescendants, android.database.IContentObserver observer, int userHandle) + // public void unregisterContentObserver(android.database.IContentObserver observer) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2.2_r1/android/content/ContentService.java + // public List getCurrentSyncsAsUser(int userId) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/content/IContentService.java + + // public Bundle call(String method, String request, Bundle args) + // http://developer.android.com/reference/android/provider/Settings.html + // http://developer.android.com/reference/android/provider/Settings.Global.html + // http://developer.android.com/reference/android/provider/Settings.Secure.html + // http://developer.android.com/reference/android/provider/Settings.System.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/providers/settings/SettingsProvider.java + + // @formatter:on + + // @formatter:off + private enum Methods { + getCurrentSync, getCurrentSyncs, getSyncAdapterTypes, + openAssetFile, openFile, openAssetFileDescriptor, openFileDescriptor, openInputStream, openOutputStream, openTypedAssetFileDescriptor, + query, Srv_call, Srv_query, + Srv_getCurrentSyncs, Srv_getCurrentSyncsAsUser + }; + // @formatter:on + + // @formatter:off + public static List cProviderClassName = Arrays.asList(new String[] { + "com.android.providers.downloads.DownloadProvider", + "com.android.providers.calendar.CalendarProvider2", + "com.android.providers.contacts.CallLogProvider", + "com.android.providers.contacts.ContactsProvider2", + "com.google.android.gm.provider.PublicContentProvider", + "com.google.android.gsf.gservices.GservicesProvider", + "com.android.providers.telephony.MmsProvider", + "com.android.providers.telephony.MmsSmsProvider", + "com.android.providers.telephony.SmsProvider", + "com.android.providers.telephony.TelephonyProvider", + "com.android.providers.userdictionary.UserDictionaryProvider", + "com.android.providers.settings.SettingsProvider", + }); + // @formatter:on + + public static List getPackageInstances(String packageName, ClassLoader loader) { + if (packageName.startsWith("com.android.browser.provider")) + try { + Class.forName("com.android.browser.provider.BrowserProviderProxy", false, loader); + return getInstances("com.android.browser.provider.BrowserProviderProxy"); + } catch (ClassNotFoundException ignored) { + try { + Class.forName("com.android.browser.provider.BrowserProvider2", false, loader); + return getInstances("com.android.browser.provider.BrowserProvider2"); + } catch (ClassNotFoundException ignored2) { + Util.log(null, Log.ERROR, "Browser provider not found, package=" + packageName); + return new ArrayList(); + } + } + + else if (packageName.startsWith("com.android.email.provider")) + try { + Class.forName("com.android.email.provider.EmailProvider", false, loader); + return getInstances("com.android.email.provider.EmailProvider"); + } catch (ClassNotFoundException ignored) { + Util.log(null, Log.WARN, "E-mail provider not found, package=" + packageName); + return new ArrayList(); + } + + else if (packageName.startsWith("com.google.android.gm.provider")) + try { + Class.forName("com.google.android.gm.provider.PublicContentProvider", false, loader); + return getInstances("com.google.android.gm.provider.PublicContentProvider"); + } catch (ClassNotFoundException ignored) { + Util.log(null, Log.WARN, "G-mail provider not found, package=" + packageName); + return new ArrayList(); + } + + else { + List listHook = new ArrayList(); + for (String className : cProviderClassName) + if (className.startsWith(packageName)) + listHook.addAll(getInstances(className)); + return listHook; + } + } + + private static List getInstances(String className) { + List listHook = new ArrayList(); + + if ("com.android.providers.settings.SettingsProvider".equals(className)) + listHook.add(new XContentResolver(Methods.Srv_call, null, className)); + else + listHook.add(new XContentResolver(Methods.Srv_query, null, className)); + + return listHook; + } + + public static List getInstances(boolean server) { + List listHook = new ArrayList(); + + if (server) { + listHook.add(new XContentResolver(Methods.Srv_query, null, "com.android.internal.telephony.IccProvider")); + + listHook.add(new XContentResolver(Methods.Srv_getCurrentSyncs, PrivacyManager.cAccounts, null)); + listHook.add(new XContentResolver(Methods.Srv_getCurrentSyncsAsUser, PrivacyManager.cAccounts, null)); + } else { + listHook.add(new XContentResolver(Methods.getCurrentSync, PrivacyManager.cAccounts, false)); + listHook.add(new XContentResolver(Methods.getCurrentSyncs, PrivacyManager.cAccounts, false)); + listHook.add(new XContentResolver(Methods.getSyncAdapterTypes, PrivacyManager.cAccounts, false)); + + listHook.add(new XContentResolver(Methods.openAssetFileDescriptor, PrivacyManager.cStorage, false)); + listHook.add(new XContentResolver(Methods.openFileDescriptor, PrivacyManager.cStorage, false)); + listHook.add(new XContentResolver(Methods.openInputStream, PrivacyManager.cStorage, false)); + listHook.add(new XContentResolver(Methods.openOutputStream, PrivacyManager.cStorage, false)); + listHook.add(new XContentResolver(Methods.openTypedAssetFileDescriptor, PrivacyManager.cStorage, false)); + + listHook.add(new XContentResolver(Methods.openAssetFile, PrivacyManager.cStorage, true)); + listHook.add(new XContentResolver(Methods.openFile, PrivacyManager.cStorage, true)); + listHook.add(new XContentResolver(Methods.openTypedAssetFileDescriptor, PrivacyManager.cStorage, true)); + + listHook.add(new XContentResolver(Methods.query, null, false)); + listHook.add(new XContentResolver(Methods.query, null, true)); + } + + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getCurrentSync: + case getCurrentSyncs: + case getSyncAdapterTypes: + case openAssetFile: + case openFile: + case openAssetFileDescriptor: + case openFileDescriptor: + case openInputStream: + case openOutputStream: + case openTypedAssetFileDescriptor: + // Do nothing + break; + + case Srv_call: + break; + + case query: + case Srv_query: + handleUriBefore(param); + break; + + case Srv_getCurrentSyncs: + case Srv_getCurrentSyncsAsUser: + // Do nothing + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getCurrentSync: + if (isRestricted(param)) + param.setResult(null); + break; + + case getCurrentSyncs: + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getSyncAdapterTypes: + if (isRestricted(param)) + param.setResult(new SyncAdapterType[0]); + break; + + case openAssetFileDescriptor: + case openFileDescriptor: + case openInputStream: + case openOutputStream: + case openTypedAssetFileDescriptor: + case openAssetFile: + case openFile: + if (param.args.length > 0 && param.args[0] instanceof Uri) { + String uri = ((Uri) param.args[0]).toString(); + if (isRestrictedExtra(param, uri)) + param.setThrowable(new FileNotFoundException("XPrivacy")); + } + break; + + case Srv_call: + handleCallAfter(param); + break; + + case query: + case Srv_query: + handleUriAfter(param); + break; + + case Srv_getCurrentSyncs: + case Srv_getCurrentSyncsAsUser: + if (param.getResult() != null) + if (isRestricted(param)) { + int uid = Binder.getCallingUid(); + @SuppressWarnings("unchecked") + List listSync = (List) param.getResult(); + List listFiltered = new ArrayList(); + for (SyncInfo sync : listSync) + if (XAccountManager.isAccountAllowed(sync.account, uid)) + listFiltered.add(sync); + param.setResult(listFiltered); + } + break; + } + } + + @SuppressLint("DefaultLocale") + private void handleUriBefore(XParam param) throws Throwable { + // Check URI + if (param.args.length > 1 && param.args[0] instanceof Uri) { + String uri = ((Uri) param.args[0]).toString().toLowerCase(); + String[] projection = (param.args[1] instanceof String[] ? (String[]) param.args[1] : null); + + if (uri.startsWith("content://com.android.contacts/contacts/name_phone_or_email")) { + // Do nothing + + } else if (uri.startsWith("content://com.android.contacts/") + && !uri.equals("content://com.android.contacts/")) { + String[] components = uri.replace("content://com.android.", "").split("/"); + String methodName = components[0] + "/" + components[1].split("\\?")[0]; + if (methodName.equals("contacts/contacts") || methodName.equals("contacts/data") + || methodName.equals("contacts/phone_lookup") || methodName.equals("contacts/raw_contacts")) + if (isRestrictedExtra(param, PrivacyManager.cContacts, methodName, uri)) { + // Get ID from URL if any + int urlid = -1; + if ((methodName.equals("contacts/contacts") || methodName.equals("contacts/phone_lookup")) + && components.length > 2 && TextUtils.isDigitsOnly(components[2])) + urlid = Integer.parseInt(components[2]); + + // Modify projection + boolean added = false; + if (projection != null && urlid < 0) { + List listProjection = new ArrayList(); + listProjection.addAll(Arrays.asList(projection)); + String cid = getIdForUri(uri); + if (cid != null && !listProjection.contains(cid)) { + added = true; + listProjection.add(cid); + } + param.args[1] = listProjection.toArray(new String[0]); + } + if (added) + param.setObjectExtra("column_added", added); + } + } + } + } + + @SuppressLint("DefaultLocale") + private void handleUriAfter(XParam param) throws Throwable { + // Check URI + if (param.args.length > 1 && param.args[0] instanceof Uri && param.getResult() != null) { + String uri = ((Uri) param.args[0]).toString().toLowerCase(); + String[] projection = (param.args[1] instanceof String[] ? (String[]) param.args[1] : null); + String selection = (param.args[2] instanceof String ? (String) param.args[2] : null); + Cursor cursor = (Cursor) param.getResult(); + + if (uri.startsWith("content://applications")) { + // Applications provider: allow selected applications + if (isRestrictedExtra(param, PrivacyManager.cSystem, "ApplicationsProvider", uri)) { + MatrixCursor result = new MatrixCursor(cursor.getColumnNames()); + while (cursor.moveToNext()) { + int colPackage = cursor.getColumnIndex("package"); + String packageName = (colPackage < 0 ? null : cursor.getString(colPackage)); + if (packageName != null && XPackageManager.isPackageAllowed(0, packageName)) + copyColumns(cursor, result); + } + result.respond(cursor.getExtras()); + param.setResult(result); + cursor.close(); + } + + } else if (uri.startsWith("content://com.google.android.gsf.gservices")) { + // Google services provider: block only android_id + if (param.args.length > 3 && param.args[3] != null) { + List listSelection = Arrays.asList((String[]) param.args[3]); + if (listSelection.contains("android_id")) + if (isRestrictedExtra(param, PrivacyManager.cIdentification, "GservicesProvider", uri)) { + int ikey = cursor.getColumnIndex("key"); + int ivalue = cursor.getColumnIndex("value"); + if (ikey == 0 && ivalue == 1 && cursor.getColumnCount() == 2) { + MatrixCursor result = new MatrixCursor(cursor.getColumnNames()); + while (cursor.moveToNext()) { + if ("android_id".equals(cursor.getString(ikey)) && cursor.getString(ivalue) != null) + result.addRow(new Object[] { "android_id", + PrivacyManager.getDefacedProp(Binder.getCallingUid(), "GSF_ID") }); + else + copyColumns(cursor, result); + } + result.respond(cursor.getExtras()); + param.setResult(result); + cursor.close(); + } else + Util.log(this, Log.ERROR, + "Unexpected result uri=" + uri + " columns=" + cursor.getColumnNames()); + } + } + + } else if (uri.startsWith("content://com.android.contacts/contacts/name_phone_or_email")) { + + // Do nothing + + } else if (uri.startsWith("content://com.android.contacts/") + && !uri.equals("content://com.android.contacts/")) { + // Contacts provider: allow selected contacts + String[] components = uri.replace("content://com.android.", "").split("/"); + String methodName = components[0] + "/" + components[1].split("\\?")[0]; + if (methodName.equals("contacts/contacts") || methodName.equals("contacts/data") + || methodName.equals("contacts/phone_lookup") || methodName.equals("contacts/raw_contacts")) { + if (isRestrictedExtra(param, PrivacyManager.cContacts, methodName, uri)) { + // Get ID from URL if any + int urlid = -1; + if ((methodName.equals("contacts/contacts") || methodName.equals("contacts/phone_lookup")) + && components.length > 2 && TextUtils.isDigitsOnly(components[2])) + urlid = Integer.parseInt(components[2]); + + // Modify column names back + Object column_added = param.getObjectExtra("column_added"); + boolean added = (column_added == null ? false : (Boolean) param.getObjectExtra("column_added")); + + List listColumn = new ArrayList(); + listColumn.addAll(Arrays.asList(cursor.getColumnNames())); + if (added) + listColumn.remove(listColumn.size() - 1); + + // Get blacklist setting + int uid = Binder.getCallingUid(); + boolean blacklist = PrivacyManager + .getSettingBool(-uid, PrivacyManager.cSettingBlacklist, false); + + MatrixCursor result = new MatrixCursor(listColumn.toArray(new String[0])); + + // Filter rows + String cid = getIdForUri(uri); + int iid = (cid == null ? -1 : cursor.getColumnIndex(cid)); + if (iid >= 0 || urlid >= 0) + while (cursor.moveToNext()) { + // Check if allowed + long id = (urlid >= 0 ? urlid : cursor.getLong(iid)); + boolean allowed = PrivacyManager.getSettingBool(-uid, Meta.cTypeContact, + Long.toString(id), false); + if (blacklist) + allowed = !allowed; + if (allowed) + copyColumns(cursor, result, listColumn.size()); + } + else + Util.log(this, Log.WARN, "ID missing URI=" + uri + " added=" + added + "/" + cid + + " columns=" + TextUtils.join(",", cursor.getColumnNames()) + " projection=" + + (projection == null ? "null" : TextUtils.join(",", projection)) + " selection=" + + selection); + + result.respond(cursor.getExtras()); + param.setResult(result); + cursor.close(); + } + } else { + methodName = null; + if (uri.startsWith("content://com.android.contacts/profile")) + methodName = "contacts/profile"; + else + methodName = "ContactsProvider2"; // fall-back + + if (methodName != null) + if (isRestrictedExtra(param, PrivacyManager.cContacts, methodName, uri)) { + // Return empty cursor + MatrixCursor result = new MatrixCursor(cursor.getColumnNames()); + result.respond(cursor.getExtras()); + param.setResult(result); + cursor.close(); + } + } + + } else { + // Other uri restrictions + String restrictionName = null; + String methodName = null; + if (uri.startsWith("content://browser")) { + restrictionName = PrivacyManager.cBrowser; + methodName = "BrowserProvider2"; + } + + else if (uri.startsWith("content://com.android.calendar")) { + restrictionName = PrivacyManager.cCalendar; + methodName = "CalendarProvider2"; + } + + else if (uri.startsWith("content://call_log")) { + restrictionName = PrivacyManager.cCalling; + methodName = "CallLogProvider"; + } + + else if (uri.startsWith("content://contacts/people")) { + restrictionName = PrivacyManager.cContacts; + methodName = "contacts/people"; + } + + else if (uri.startsWith("content://downloads")) { + restrictionName = PrivacyManager.cBrowser; + methodName = "Downloads"; + } + + else if (uri.startsWith("content://com.android.email.provider")) { + restrictionName = PrivacyManager.cEMail; + methodName = "EMailProvider"; + } + + else if (uri.startsWith("content://com.google.android.gm")) { + restrictionName = PrivacyManager.cEMail; + methodName = "GMailProvider"; + } + + else if (uri.startsWith("content://icc")) { + restrictionName = PrivacyManager.cContacts; + methodName = "IccProvider"; + } + + else if (uri.startsWith("content://mms")) { + restrictionName = PrivacyManager.cMessages; + methodName = "MmsProvider"; + } + + else if (uri.startsWith("content://mms-sms")) { + restrictionName = PrivacyManager.cMessages; + methodName = "MmsSmsProvider"; + } + + else if (uri.startsWith("content://sms")) { + restrictionName = PrivacyManager.cMessages; + methodName = "SmsProvider"; + } + + else if (uri.startsWith("content://telephony")) { + restrictionName = PrivacyManager.cPhone; + methodName = "TelephonyProvider"; + } + + else if (uri.startsWith("content://user_dictionary")) { + restrictionName = PrivacyManager.cDictionary; + methodName = "UserDictionary"; + } + + else if (uri.startsWith("content://com.android.voicemail")) { + restrictionName = PrivacyManager.cMessages; + methodName = "VoicemailContentProvider"; + } + + // Check if know / restricted + if (restrictionName != null && methodName != null) { + if (isRestrictedExtra(param, restrictionName, methodName, uri)) { + // Return empty cursor + MatrixCursor result = new MatrixCursor(cursor.getColumnNames()); + result.respond(cursor.getExtras()); + param.setResult(result); + cursor.close(); + } + } + } + } + } + + private void handleCallAfter(XParam param) throws Throwable { + if (param.args.length > 1 && param.args[0] instanceof String && param.args[1] instanceof String) { + String method = (String) param.args[0]; + String request = (String) param.args[1]; + + if ("GET_secure".equals(method)) { + if (Settings.Secure.ANDROID_ID.equals(request)) { + if (!hasEmptyValue(param.getResult())) + if (isRestricted(param, PrivacyManager.cIdentification, "Srv_Android_ID")) { + int uid = Binder.getCallingUid(); + String value = (String) PrivacyManager.getDefacedProp(uid, "ANDROID_ID"); + Bundle bundle = new Bundle(1); + bundle.putString("value", value); + param.setResult(bundle); + } + + } + + } else if ("GET_system".equals(method)) { + // Do nothing + + } else if ("GET_global".equals(method)) { + if ("default_dns_server".equals(request)) { + if (!hasEmptyValue(param.getResult())) + if (isRestricted(param, PrivacyManager.cNetwork, "Srv_Default_DNS")) { + int uid = Binder.getCallingUid(); + InetAddress value = (InetAddress) PrivacyManager.getDefacedProp(uid, "InetAddress"); + Bundle bundle = new Bundle(1); + bundle.putString("value", value.getHostAddress()); + param.setResult(bundle); + } + + } else if ("wifi_country_code".equals(request)) { + if (!hasEmptyValue(param.getResult())) + if (isRestricted(param, PrivacyManager.cNetwork, "Srv_WiFi_Country")) { + int uid = Binder.getCallingUid(); + String value = (String) PrivacyManager.getDefacedProp(uid, "CountryIso"); + Bundle bundle = new Bundle(1); + bundle.putString("value", value == null ? null : value.toLowerCase(Locale.ROOT)); + param.setResult(bundle); + } + } + } + } + } + + // Helper methods + + private boolean hasEmptyValue(Object result) { + Bundle bundle = (Bundle) result; + if (bundle == null) + return true; + if (!bundle.containsKey("value")) + return true; + return (bundle.get("value") == null); + } + + private String getIdForUri(String uri) { + if (uri.startsWith("content://com.android.contacts/contacts")) + return "_id"; + else if (uri.startsWith("content://com.android.contacts/data")) + return "contact_id"; + else if (uri.startsWith("content://com.android.contacts/phone_lookup")) + return "_id"; + else if (uri.startsWith("content://com.android.contacts/raw_contacts")) + return "contact_id"; + else + Util.log(this, Log.ERROR, "Unexpected uri=" + uri); + return null; + } + + private void copyColumns(Cursor cursor, MatrixCursor result) { + copyColumns(cursor, result, cursor.getColumnCount()); + } + + private void copyColumns(Cursor cursor, MatrixCursor result, int count) { + try { + Object[] columns = new Object[count]; + for (int i = 0; i < count; i++) + switch (cursor.getType(i)) { + case Cursor.FIELD_TYPE_NULL: + columns[i] = null; + break; + case Cursor.FIELD_TYPE_INTEGER: + columns[i] = cursor.getInt(i); + break; + case Cursor.FIELD_TYPE_FLOAT: + columns[i] = cursor.getFloat(i); + break; + case Cursor.FIELD_TYPE_STRING: + columns[i] = cursor.getString(i); + break; + case Cursor.FIELD_TYPE_BLOB: + columns[i] = cursor.getBlob(i); + break; + default: + Util.log(this, Log.WARN, "Unknown cursor data type=" + cursor.getType(i)); + } + result.addRow(columns); + } catch (Throwable ex) { + Util.bug(this, ex); + } + } + + @SuppressWarnings("unused") + private void _dumpCursor(String uri, Cursor cursor) { + _dumpHeader(uri, cursor); + int i = 0; + while (cursor.moveToNext() && i++ < 10) + _dumpColumns(cursor, ""); + cursor.moveToFirst(); + } + + private void _dumpHeader(String uri, Cursor cursor) { + Util.log(this, Log.WARN, TextUtils.join(", ", cursor.getColumnNames()) + " uri=" + uri); + } + + private void _dumpColumns(Cursor cursor, String msg) { + String[] columns = new String[cursor.getColumnCount()]; + for (int i = 0; i < cursor.getColumnCount(); i++) + switch (cursor.getType(i)) { + case Cursor.FIELD_TYPE_NULL: + columns[i] = null; + break; + case Cursor.FIELD_TYPE_INTEGER: + columns[i] = Integer.toString(cursor.getInt(i)); + break; + case Cursor.FIELD_TYPE_FLOAT: + columns[i] = Float.toString(cursor.getFloat(i)); + break; + case Cursor.FIELD_TYPE_STRING: + columns[i] = cursor.getString(i); + break; + case Cursor.FIELD_TYPE_BLOB: + columns[i] = "[blob]"; + break; + default: + Util.log(this, Log.WARN, "Unknown cursor data type=" + cursor.getType(i)); + } + Util.log(this, Log.WARN, TextUtils.join(", ", columns) + " " + msg); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XContextImpl.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XContextImpl.java new file mode 100644 index 0000000..bf7075e --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XContextImpl.java @@ -0,0 +1,59 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +public class XContextImpl extends XHook { + private Methods mMethod; + + private XContextImpl(Methods method, String restrictionName) { + super(restrictionName, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return "android.app.ContextImpl"; + } + + // @formatter:off + + // public PackageManager getPackageManager() + // public Object getSystemService(String name) + // frameworks/base/core/java/android/app/ContextImpl.java + + // @formatter:on + + private enum Methods { + getPackageManager, getSystemService + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XContextImpl(Methods.getPackageManager, null)); + listHook.add(new XContextImpl(Methods.getSystemService, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getPackageManager: + if (param.getResult() != null) + XPrivacy.handleGetSystemService("PackageManager", param.getResult().getClass().getName(), getSecret()); + break; + + case getSystemService: + if (param.args.length > 0 && param.args[0] instanceof String && param.getResult() != null) { + String name = (String) param.args[0]; + Object instance = param.getResult(); + XPrivacy.handleGetSystemService(name, instance.getClass().getName(), getSecret()); + } + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XEnvironment.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XEnvironment.java new file mode 100644 index 0000000..f2170b7 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XEnvironment.java @@ -0,0 +1,59 @@ +package biz.bokhorst.xprivacy; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import android.os.Build; +import android.os.Environment; + +import biz.bokhorst.xprivacy.XHook; + +public class XEnvironment extends XHook { + private Methods mMethod; + + private XEnvironment(Methods method, String restrictionName) { + super(restrictionName, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return "android.os.Environment"; + } + + // public static String getExternalStorageState() + // frameworks/base/core/java/android/os/Environment.java + // http://developer.android.com/reference/android/os/Environment.html + + private enum Methods { + getExternalStorageState + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XEnvironment(Methods.getExternalStorageState, PrivacyManager.cStorage)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getExternalStorageState: + if (param.getResult() != null) { + String extra = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + if (param.args.length > 0 && param.args[0] instanceof File) + extra = ((File) param.args[0]).getAbsolutePath(); + + if (isRestrictedExtra(param, extra)) + param.setResult(Environment.MEDIA_UNMOUNTED); + } + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XFusedLocationApi.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XFusedLocationApi.java new file mode 100644 index 0000000..0da1da5 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XFusedLocationApi.java @@ -0,0 +1,130 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import android.app.PendingIntent; +import android.location.Location; +import android.os.Binder; +import android.util.Log; + +public class XFusedLocationApi extends XHook { + private Methods mMethod; + private String mClassName; + private static final Map mMapProxy = new WeakHashMap(); + + private XFusedLocationApi(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), "GMS5." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // Location getLastLocation(GoogleApiClient client) + // abstract PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener) + // abstract PendingResult removeLocationUpdates(GoogleApiClient client, PendingIntent callbackIntent) + // abstract PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener, Looper looper) + // abstract PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener) + // abstract PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, PendingIntent callbackIntent) + // https://developer.android.com/reference/com/google/android/gms/location/FusedLocationProviderApi.html + + // @formatter:on + + private enum Methods { + getLastLocation, removeLocationUpdates, requestLocationUpdates + }; + + public static List getInstances(Object instance) { + String className = instance.getClass().getName(); + Util.log(null, Log.INFO, "Hooking FusedLocationApi class=" + className + " uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XFusedLocationApi(Methods.getLastLocation, PrivacyManager.cLocation, className)); + listHook.add(new XFusedLocationApi(Methods.removeLocationUpdates, null, className)); + listHook.add(new XFusedLocationApi(Methods.requestLocationUpdates, PrivacyManager.cLocation, className)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getLastLocation: + // Do nothing + break; + + case removeLocationUpdates: + if (param.args.length > 1) + if (param.args[1] instanceof PendingIntent) { + if (isRestricted(param, PrivacyManager.cLocation, "GMS5.requestLocationUpdates")) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + } else + synchronized (mMapProxy) { + if (mMapProxy.containsKey(param.args[1])) + param.args[1] = mMapProxy.get(param.args[1]); + } + break; + + case requestLocationUpdates: + if (param.args.length > 2) + if (isRestricted(param)) + if (param.args[2] instanceof PendingIntent) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + else if (param.thisObject != null && param.args[2] != null) { + // Create proxy + ClassLoader cl = param.thisObject.getClass().getClassLoader(); + Class ll = Class.forName("com.google.android.gms.location.LocationListener", false, cl); + InvocationHandler ih = new OnLocationChangedHandler(Binder.getCallingUid(), param.args[2]); + Object proxy = Proxy.newProxyInstance(cl, new Class[] { ll }, ih); + + // Use proxy + synchronized (mMapProxy) { + mMapProxy.put(param.args[2], proxy); + } + param.args[2] = proxy; + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getLastLocation: + Location location = (Location) param.getResult(); + if (location != null && isRestricted(param)) + param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); + break; + + case removeLocationUpdates: + case requestLocationUpdates: + // Do nothing + break; + } + } + + private class OnLocationChangedHandler implements InvocationHandler { + private int mUid; + private Object mTarget; + + public OnLocationChangedHandler(int uid, Object target) { + mUid = uid; + mTarget = target; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("onLocationChanged".equals(method.getName())) + args[0] = PrivacyManager.getDefacedLocation(mUid, (Location) args[0]); + return method.invoke(mTarget, args); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleApiClient.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleApiClient.java new file mode 100644 index 0000000..7f58596 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleApiClient.java @@ -0,0 +1,122 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XGoogleApiClient extends XHook { + private Methods mMethod; + + private XGoogleApiClient(Methods method, String restrictionName) { + super(restrictionName, method.name(), "GMS5." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.gms.common.api.GoogleApiClient$Builder"; + } + + // @formatter:off + + // GoogleApiClient.Builder addConnectionCallbacks(GoogleApiClient.ConnectionCallbacks listener) + // https://developer.android.com/reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html + + // https://developer.android.com/reference/com/google/android/gms/common/api/PendingResult.html + // https://developer.android.com/reference/com/google/android/gms/common/api/Status.html + // https://developer.android.com/reference/com/google/android/gms/common/api/ResultCallback.html + + // @formatter:on + + private enum Methods { + addConnectionCallbacks + }; + + public static List getInstances() { + Util.log(null, Log.INFO, "Loaded GoogleApiClient$Builder uid=" + Binder.getCallingUid()); + List listHook = new ArrayList(); + listHook.add(new XGoogleApiClient(Methods.addConnectionCallbacks, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case addConnectionCallbacks: + if (param.args.length > 0 && param.args[0] != null) { + Class clazz = param.args[0].getClass(); + if (PrivacyManager.getTransient(clazz.getName(), null) == null) { + PrivacyManager.setTransient(clazz.getName(), Boolean.toString(true)); + + XPrivacy.hookAll(XConnectionCallbacks.getInstances(param.args[0]), clazz.getClassLoader(), + getSecret(), true); + } + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } + + public static Object getPendingResult(ClassLoader loader) throws Throwable { + InvocationHandler ih = new PendingResultHandler(loader); + Class pr = Class.forName("com.google.android.gms.common.api.PendingResult", false, loader); + return Proxy.newProxyInstance(loader, new Class[] { pr }, ih); + } + + private static class PendingResultHandler implements InvocationHandler { + private ClassLoader mLoader; + private boolean mCancelled = false; + + public PendingResultHandler(ClassLoader loader) { + mLoader = loader; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("await".equals(method.getName())) { + // abstract R await() + // abstract R await(long time, TimeUnit units) + return getStatus(mLoader); + + } else if ("cancel".equals(method.getName())) { + // abstract void cancel() + mCancelled = true; + return null; + + } else if ("isCanceled".equals(method.getName())) { + // abstract boolean isCanceled() + return mCancelled; + + } else if ("setResultCallback".equals(method.getName())) { + // abstract void setResultCallback(ResultCallback callback, + // long time, TimeUnit units) + // abstract void setResultCallback(ResultCallback callback) + Object callback = args[0]; + if (callback != null) { + // abstract void onResult(R result) + Class cStatus = Class.forName("com.google.android.gms.common.api.Status", false, mLoader); + callback.getClass().getMethod("onResult", cStatus).invoke(callback, getStatus(mLoader)); + } + return null; + } + + return null; + } + } + + private static Object getStatus(ClassLoader loader) throws Throwable { + // public com.google.android.gms.common.api.Status(int) + Class cStatus = Class.forName("com.google.android.gms.common.api.Status", false, loader); + Constructor iStatus = cStatus.getConstructor(int.class); + Object status = iStatus.newInstance(0); // SUCCESS + return status; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleAuthUtil.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleAuthUtil.java new file mode 100644 index 0000000..a3644b2 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleAuthUtil.java @@ -0,0 +1,66 @@ +package biz.bokhorst.xprivacy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import android.accounts.Account; +import android.util.Log; + +public class XGoogleAuthUtil extends XHook { + private Methods mMethod; + + private XGoogleAuthUtil(Methods method, String restrictionName, String specifier) { + super(restrictionName, method.name(), specifier); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.gms.auth.GoogleAuthUtil"; + } + + // @formatter:off + + // static String getToken(Context context, String accountName, String scope) + // static String getToken(Context context, String accountName, String scope, Bundle extras) + // static String getTokenWithNotification(Context context, String accountName, String scope, Bundle extras) + // static String getTokenWithNotification(Context context, String accountName, String scope, Bundle extras, Intent callback) + // static String getTokenWithNotification(Context context, String accountName, String scope, Bundle extras, String authority, Bundle syncBundle) + // https://developer.android.com/reference/com/google/android/gms/auth/GoogleAuthUtil.html + + // @formatter:on + + private enum Methods { + getToken, getTokenWithNotification + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XGoogleAuthUtil(Methods.getToken, PrivacyManager.cAccounts, "getTokenGoogle")); + listHook.add(new XGoogleAuthUtil(Methods.getTokenWithNotification, PrivacyManager.cAccounts, + "getTokenWithNotificationGoogle")); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.getToken || mMethod == Methods.getTokenWithNotification) { + if (param.args.length > 1) { + String accountName = null; + if (param.args[1] instanceof String) + accountName = (String) param.args[1]; + else if (param.args[1] instanceof Account) + accountName = ((Account) param.args[1]).type; + if (param.getResult() != null && isRestrictedExtra(param, accountName)) + param.setThrowable(new IOException("XPrivacy")); + } + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleMapV1.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleMapV1.java new file mode 100644 index 0000000..a773e41 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleMapV1.java @@ -0,0 +1,52 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +public class XGoogleMapV1 extends XHook { + private Methods mMethod; + + private XGoogleMapV1(Methods method, String restrictionName) { + super(restrictionName, method.name(), String.format("MapV1.%s", method.name())); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.maps.MyLocationOverlay"; + } + + // boolean enableMyLocation() + // void disableMyLocation() + // https://developers.google.com/maps/documentation/android/v1/reference/index + + private enum Methods { + enableMyLocation, disableMyLocation + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XGoogleMapV1(Methods.enableMyLocation, PrivacyManager.cLocation)); + listHook.add(new XGoogleMapV1(Methods.disableMyLocation, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case enableMyLocation: + if (isRestricted(param)) + param.setResult(false); + break; + + case disableMyLocation: + if (isRestricted(param, PrivacyManager.cLocation, "MapV1.enableMyLocation")) + param.setResult(null); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleMapV2.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleMapV2.java new file mode 100644 index 0000000..5051300 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XGoogleMapV2.java @@ -0,0 +1,105 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import android.location.Location; +import android.os.Binder; + +public class XGoogleMapV2 extends XHook { + private Methods mMethod; + + private XGoogleMapV2(Methods method, String restrictionName) { + super(restrictionName, method.name(), String.format("MapV2.%s", method.name())); + mMethod = method; + } + + public String getClassName() { + if (mMethod == Methods.getPosition) + return "com.google.android.gms.maps.model.Marker"; + else + return "com.google.android.gms.maps.GoogleMap"; + } + + // @formatter:off + + // final Location getMyLocation() + // final void setLocationSource(LocationSource source) + // final void setOnMapClickListener(GoogleMap.OnMapClickListener listener) + // final void setOnMapLongClickListener(GoogleMap.OnMapLongClickListener listener) + // final void setOnMyLocationChangeListener(GoogleMap.OnMyLocationChangeListener listener) + // http://developer.android.com/reference/com/google/android/gms/maps/GoogleMap.html + + // public LatLng getPosition () + // http://developer.android.com/reference/com/google/android/gms/maps/model/Marker.html + // http://developer.android.com/reference/com/google/android/gms/maps/model/LatLng.html + + // @formatter:on + + private enum Methods { + getMyLocation, getPosition, setLocationSource, setOnMapClickListener, setOnMapLongClickListener, setOnMyLocationChangeListener + }; + + public static List getInstances() { + List listHook = new ArrayList(); + for (Methods method : Methods.values()) + listHook.add(new XGoogleMapV2(method, PrivacyManager.cLocation)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getMyLocation: + // Do nothing + break; + + case getPosition: + // Do nothing + break; + + case setLocationSource: + case setOnMapClickListener: + case setOnMapLongClickListener: + case setOnMyLocationChangeListener: + if (isRestricted(param)) + param.setResult(null); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getMyLocation: + if (param.getResult() != null) + if (isRestricted(param)) { + Location originalLocation = (Location) param.getResult(); + Location fakeLocation = PrivacyManager.getDefacedLocation(Binder.getCallingUid(), originalLocation); + param.setResult(fakeLocation); + } + break; + + case getPosition: + if (param.getResult() != null) + if (isRestricted(param)) { + Location fakeLocation = PrivacyManager.getDefacedLocation(Binder.getCallingUid(), null); + Field fLat = param.getResult().getClass().getField("latitude"); + Field fLon = param.getResult().getClass().getField("longitude"); + fLat.setAccessible(true); + fLon.setAccessible(true); + fLat.set(param.getResult(), fakeLocation.getLatitude()); + fLon.set(param.getResult(), fakeLocation.getLongitude()); + } + break; + + case setLocationSource: + case setOnMapClickListener: + case setOnMapLongClickListener: + case setOnMyLocationChangeListener: + // Do nothing + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XHook.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XHook.java new file mode 100644 index 0000000..decb69e --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XHook.java @@ -0,0 +1,123 @@ +package biz.bokhorst.xprivacy; + +import android.annotation.SuppressLint; +import android.os.Binder; + +public abstract class XHook { + private String mRestrictionName; + private String mMethodName; + private String mSpecifier; + private String mSecret; + + protected XHook(String restrictionName, String methodName, String specifier) { + mRestrictionName = restrictionName; + mMethodName = methodName; + mSpecifier = specifier; + } + + abstract public String getClassName(); + + public boolean isVisible() { + return true; + } + + public String getRestrictionName() { + return mRestrictionName; + } + + public String getMethodName() { + return mMethodName; + } + + public String getSpecifier() { + return (mSpecifier == null ? mMethodName : mSpecifier); + } + + public void setSecret(String secret) { + mSecret = secret; + } + + protected String getSecret() { + return mSecret; + } + + abstract protected void before(XParam param) throws Throwable; + + abstract protected void after(XParam param) throws Throwable; + + protected boolean isRestricted(XParam param) throws Throwable { + return isRestricted(param, getSpecifier()); + } + + protected boolean isRestrictedExtra(XParam param, String extra) throws Throwable { + int uid = Binder.getCallingUid(); + return PrivacyManager.getRestrictionExtra(this, uid, mRestrictionName, getSpecifier(), extra, mSecret); + } + + protected boolean isRestrictedExtra(XParam param, String methodName, String extra) throws Throwable { + int uid = Binder.getCallingUid(); + return PrivacyManager.getRestrictionExtra(this, uid, mRestrictionName, methodName, extra, mSecret); + } + + protected boolean isRestrictedExtra(XParam param, String restrictionName, String methodName, String extra) + throws Throwable { + int uid = Binder.getCallingUid(); + return PrivacyManager.getRestrictionExtra(this, uid, restrictionName, methodName, extra, mSecret); + } + + protected boolean isRestrictedExtra(int uid, String restrictionName, String methodName, String extra) + throws Throwable { + return PrivacyManager.getRestrictionExtra(this, uid, restrictionName, methodName, extra, mSecret); + } + + protected boolean isRestrictedValue(int uid, String value) throws Throwable { + return PrivacyManager.getRestrictionExtra(this, uid, mRestrictionName, getSpecifier(), null, value, mSecret); + } + + protected boolean isRestrictedValue(XParam param, String value) throws Throwable { + int uid = Binder.getCallingUid(); + return PrivacyManager.getRestrictionExtra(this, uid, mRestrictionName, getSpecifier(), null, value, mSecret); + } + + protected boolean isRestrictedValue(int uid, String methodName, String value) throws Throwable { + return PrivacyManager.getRestrictionExtra(this, uid, mRestrictionName, methodName, null, value, mSecret); + } + + protected boolean isRestrictedValue(int uid, String restrictionName, String methodName, String value) + throws Throwable { + return PrivacyManager.getRestrictionExtra(this, uid, restrictionName, methodName, null, value, mSecret); + } + + protected boolean isRestrictedExtraValue(int uid, String restrictionName, String methodName, String extra, + String value) throws Throwable { + return PrivacyManager.getRestrictionExtra(this, uid, restrictionName, methodName, extra, value, mSecret); + } + + protected boolean isRestricted(XParam param, String methodName) throws Throwable { + int uid = Binder.getCallingUid(); + return PrivacyManager.getRestriction(this, uid, mRestrictionName, methodName, mSecret); + } + + protected boolean isRestricted(XParam param, String restrictionName, String methodName) throws Throwable { + int uid = Binder.getCallingUid(); + return PrivacyManager.getRestriction(this, uid, restrictionName, methodName, mSecret); + } + + protected boolean getRestricted(int uid) throws Throwable { + return PrivacyManager.getRestriction(this, uid, mRestrictionName, getSpecifier(), mSecret); + } + + protected boolean getRestricted(int uid, String methodName) throws Throwable { + return PrivacyManager.getRestriction(this, uid, mRestrictionName, methodName, mSecret); + } + + protected boolean getRestricted(int uid, String restrictionName, String methodName) throws Throwable { + return PrivacyManager.getRestriction(this, uid, restrictionName, methodName, mSecret); + } + + @Override + @SuppressLint("FieldGetter") + public String toString() { + return getRestrictionName() + "/" + getSpecifier() + " (" + getClassName() + ")"; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XInetAddress.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XInetAddress.java new file mode 100644 index 0000000..e1a1007 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XInetAddress.java @@ -0,0 +1,80 @@ +package biz.bokhorst.xprivacy; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +public class XInetAddress extends XHook { + @SuppressWarnings("unused") + private Methods mMethod; + + private XInetAddress(Methods method, String restrictionName, String specifier) { + super(restrictionName, method.name(), "InetAddress." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "java.net.InetAddress"; + } + + // public static InetAddress[] getAllByName(String host) + // public static InetAddress[] getAllByNameOnNet(String host, int netId) + // public static InetAddress getByAddress(byte[] ipAddress) + // public static InetAddress getByAddress(String hostName, byte[] ipAddress) + // public static InetAddress getByName(String host) + // public static InetAddress getByNameOnNet(String host, int netId) + // libcore/luni/src/main/java/java/net/InetAddress.java + // http://developer.android.com/reference/java/net/InetAddress.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/net/Network.java + + private enum Methods { + getAllByName, getAllByNameOnNet, getByAddress, getByName, getByNameOnNet + }; + + public static List getInstances() { + List listHook = new ArrayList(); + for (Methods addr : Methods.values()) + listHook.add(new XInetAddress(addr, PrivacyManager.cInternet, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + Object result = param.getResult(); + if (result != null) { + // Get addresses + InetAddress[] addresses; + if (result instanceof InetAddress) + addresses = new InetAddress[] { (InetAddress) result }; + else if (result instanceof InetAddress[]) + addresses = (InetAddress[]) result; + else + addresses = new InetAddress[0]; + + // Check if restricted + boolean restrict = false; + for (InetAddress address : addresses) + if (!address.isLoopbackAddress()) { + restrict = true; + break; + } + + // Restrict + if (restrict) + if (param.args.length > 0 && param.args[0] instanceof String) { + if (isRestrictedExtra(param, (String) param.args[0])) + param.setThrowable(new UnknownHostException("XPrivacy")); + + } else { + if (isRestricted(param)) + param.setThrowable(new UnknownHostException("XPrivacy")); + } + } + } +} \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XInputDevice.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XInputDevice.java new file mode 100644 index 0000000..543b58b --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XInputDevice.java @@ -0,0 +1,55 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; + +public class XInputDevice extends XHook { + private Methods mMethod; + + private XInputDevice(Methods method, String restrictionName) { + super(restrictionName, method.name(), "InputDevice." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.view.InputDevice"; + } + + // @formatter:off + + // public String getDescriptor() + // public String getName() + // frameworks/base/core/java/android/view/InputDevice.java + // http://developer.android.com/reference/android/view/InputDevice.html + + // @formatter:on + + private enum Methods { + getDescriptor, getName + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XInputDevice(Methods.getDescriptor, PrivacyManager.cIdentification)); + listHook.add(new XInputDevice(Methods.getName, PrivacyManager.cIdentification)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getDescriptor: + case getName: + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "DeviceDescriptor")); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIntentFirewall.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIntentFirewall.java new file mode 100644 index 0000000..c6707c6 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIntentFirewall.java @@ -0,0 +1,158 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.provider.Telephony; +import android.service.notification.NotificationListenerService; +import android.telephony.TelephonyManager; + +@SuppressLint("InlinedApi") +public class XIntentFirewall extends XHook { + private Methods mMethod; + private static Map mapIntentRestriction = new HashMap(); + + static { + // Intent receive: calling + mapIntentRestriction.put(Intent.ACTION_NEW_OUTGOING_CALL, PrivacyManager.cCalling); + mapIntentRestriction.put(TelephonyManager.ACTION_PHONE_STATE_CHANGED, PrivacyManager.cPhone); + mapIntentRestriction.put(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, PrivacyManager.cCalling); + + // Intent receive: C2DM + mapIntentRestriction.put("com.google.android.c2dm.intent.REGISTRATION", PrivacyManager.cNotifications); + mapIntentRestriction.put("com.google.android.c2dm.intent.RECEIVE", PrivacyManager.cNotifications); + + // Intent receive: NFC + mapIntentRestriction.put(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED, PrivacyManager.cNfc); + mapIntentRestriction.put(NfcAdapter.ACTION_NDEF_DISCOVERED, PrivacyManager.cNfc); + mapIntentRestriction.put(NfcAdapter.ACTION_TAG_DISCOVERED, PrivacyManager.cNfc); + mapIntentRestriction.put(NfcAdapter.ACTION_TECH_DISCOVERED, PrivacyManager.cNfc); + + // Intent receive: SMS + mapIntentRestriction.put(Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION, PrivacyManager.cMessages); + mapIntentRestriction.put(Telephony.Sms.Intents.SMS_RECEIVED_ACTION, PrivacyManager.cMessages); + mapIntentRestriction.put(Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION, PrivacyManager.cMessages); + mapIntentRestriction.put(Telephony.Sms.Intents.SMS_DELIVER_ACTION, PrivacyManager.cMessages); + mapIntentRestriction.put(Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION, PrivacyManager.cMessages); + + // Intent receive: notifications + mapIntentRestriction.put(NotificationListenerService.SERVICE_INTERFACE, PrivacyManager.cNotifications); + + // Intent receive: package changes + mapIntentRestriction.put(Intent.ACTION_PACKAGE_ADDED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_REPLACED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_RESTARTED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_REMOVED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_CHANGED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_DATA_CLEARED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_FIRST_LAUNCH, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_FULLY_REMOVED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_PACKAGE_VERIFIED, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, PrivacyManager.cSystem); + mapIntentRestriction.put(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE, PrivacyManager.cSystem); + } + + private XIntentFirewall(Methods method) { + super(null, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return "com.android.server.firewall.IntentFirewall"; + } + + // @formatter:off + + // public boolean checkIntent(FirewallIntentResolver resolver, ComponentName resolvedComponent, int intentType, Intent intent, int callerUid, int callerPid, String resolvedType, int receivingUid) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/com/android/server/firewall/IntentFirewall.java + + // @formatter:on + + private enum Methods { + checkIntent + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XIntentFirewall(Methods.checkIntent)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case checkIntent: + if (param.args.length > 7 && param.args[3] instanceof Intent && param.args[7] instanceof Integer) { + Intent intent = (Intent) param.args[3]; + int receivingUid = (Integer) param.args[7]; + if (isIntentRestricted(receivingUid, intent)) + param.setResult(false); + } + break; + } + } + + private boolean isIntentRestricted(int uid, Intent intent) throws Throwable { + String action = intent.getAction(); + String data = intent.getDataString(); + String actionData = (action == null ? "" : action) + (data == null ? "" : ":" + data); + + if (PrivacyManager.getSettingBool(0, PrivacyManager.cSettingIntentWall, false)) + if (isRestrictedExtra(uid, "system", "IntentFirewall", actionData)) + return true; + + if (mapIntentRestriction.containsKey(action)) { + // Get restriction category + String restrictionName = mapIntentRestriction.get(action); + + if (Intent.ACTION_NEW_OUTGOING_CALL.equals(action)) { + // Outgoing call + if (intent.hasExtra(Intent.EXTRA_PHONE_NUMBER)) { + String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); + if (phoneNumber != null) + if (isRestrictedExtraValue(uid, restrictionName, action, phoneNumber, phoneNumber)) + return true; + } + + } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { + // Incoming call + if (intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)) { + String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); + if (phoneNumber != null) + if (isRestrictedExtraValue(uid, restrictionName, action, phoneNumber, phoneNumber)) + return true; + } + + } else if (PrivacyManager.cSystem.equals(restrictionName)) { + // Package event + if (isRestrictedExtra(uid, restrictionName, action, intent.getDataString())) { + String[] packageNames; + if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE) + || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) + packageNames = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + else + packageNames = new String[] { intent.getData().getSchemeSpecificPart() }; + for (String packageName : packageNames) + if (!XPackageManager.isPackageAllowed(0, packageName)) + return true; + } + + } else if (isRestrictedExtra(uid, restrictionName, action, intent.getDataString())) + return true; + + } + + return false; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIoBridge.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIoBridge.java new file mode 100644 index 0000000..7ccf846 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIoBridge.java @@ -0,0 +1,153 @@ +package biz.bokhorst.xprivacy; + +import java.io.FileNotFoundException; +import java.net.InetAddress; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; + +import android.annotation.SuppressLint; +import android.os.Binder; +import android.os.Process; +import android.text.TextUtils; +import android.util.Log; + +public class XIoBridge extends XHook { + private Methods mMethod; + private String mFileName; + + private static String mExternalStorage = null; + private static String mEmulatedSource = null; + private static String mEmulatedTarget = null; + private static String mMediaStorage = null; + private static String mSecondaryStorage = null; + + private XIoBridge(Methods method, String restrictionName) { + super(restrictionName, method.name(), null); + mMethod = method; + mFileName = null; + } + + private XIoBridge(Methods method, String restrictionName, String fileName) { + super(restrictionName, method.name(), fileName); + mMethod = method; + mFileName = fileName; + } + + public String getClassName() { + return "libcore.io.IoBridge"; + } + + // @formatter:off + + // public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException + // public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException + // public static FileDescriptor open(String path, int flags) throws FileNotFoundException + // public static FileDescriptor socket(boolean stream) throws SocketException + // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Environment.java + // https://android.googlesource.com/platform/libcore/+/android-5.0.1_r1/luni/src/main/java/libcore/io/IoBridge.java + + // @formatter:on + + private enum Methods { + open, connect + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XIoBridge(Methods.connect, PrivacyManager.cInternet)); + listHook.add(new XIoBridge(Methods.open, PrivacyManager.cStorage)); + listHook.add(new XIoBridge(Methods.open, PrivacyManager.cIdentification, "/proc")); + listHook.add(new XIoBridge(Methods.open, PrivacyManager.cIdentification, "/system/build.prop")); + listHook.add(new XIoBridge(Methods.open, PrivacyManager.cIdentification, "/sys/block/.../cid")); + listHook.add(new XIoBridge(Methods.open, PrivacyManager.cIdentification, "/sys/class/.../cid")); + return listHook; + } + + @Override + @SuppressLint("SdCardPath") + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.connect) { + if (param.args.length > 2 && param.args[1] instanceof InetAddress && param.args[2] instanceof Integer) { + InetAddress address = (InetAddress) param.args[1]; + int port = (Integer) param.args[2]; + + String hostName; + int uid = Binder.getCallingUid(); + boolean resolve = PrivacyManager.getSettingBool(uid, PrivacyManager.cSettingResolve, false); + boolean noresolve = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingNoResolve, false); + if (resolve && !noresolve) + try { + hostName = address.getHostName(); + } catch (Throwable ignored) { + hostName = address.toString(); + } + else + hostName = address.toString(); + + if (isRestrictedExtra(param, hostName + ":" + port)) + param.setThrowable(new SocketException("XPrivacy")); + } + + } else if (mMethod == Methods.open) { + if (param.args.length > 0) { + String fileName = (String) param.args[0]; + if (mFileName == null && fileName != null) { + // Get storage folders + if (mExternalStorage == null) { + mExternalStorage = System.getenv("EXTERNAL_STORAGE"); + mEmulatedSource = System.getenv("EMULATED_STORAGE_SOURCE"); + mEmulatedTarget = System.getenv("EMULATED_STORAGE_TARGET"); + mMediaStorage = System.getenv("MEDIA_STORAGE"); + mSecondaryStorage = System.getenv("SECONDARY_STORAGE"); + if (TextUtils.isEmpty(mMediaStorage)) + mMediaStorage = "/data/media"; + } + + // Check storage folders + if (fileName.startsWith("/sdcard") + || (mExternalStorage != null && fileName.startsWith(mExternalStorage)) + || (mEmulatedSource != null && fileName.startsWith(mEmulatedSource)) + || (mEmulatedTarget != null && fileName.startsWith(mEmulatedTarget)) + || (mMediaStorage != null && fileName.startsWith(mMediaStorage)) + || (mSecondaryStorage != null && fileName.startsWith(mSecondaryStorage))) + if (isRestrictedExtra(param, fileName)) + param.setThrowable(new FileNotFoundException("XPrivacy")); + + } else if (fileName.startsWith(mFileName) || mFileName.contains("...")) { + // Zygote, Android + if (Util.getAppId(Process.myUid()) == Process.SYSTEM_UID) + return; + + // Proc white list + if (mFileName.equals("/proc")) + if ("/proc/self/cmdline".equals(fileName)) + return; + + // Check if restricted + if (mFileName.contains("...")) { + String[] component = mFileName.split("\\.\\.\\."); + if (fileName.startsWith(component[0]) && fileName.endsWith(component[1])) + if (isRestricted(param, mFileName)) + param.setThrowable(new FileNotFoundException("XPrivacy")); + + } else if (mFileName.equals("/proc")) { + if (isRestrictedExtra(param, mFileName, fileName)) + param.setThrowable(new FileNotFoundException("XPrivacy")); + + } else { + if (isRestricted(param, mFileName)) + param.setThrowable(new FileNotFoundException("XPrivacy")); + } + } + } + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIpPrefix.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIpPrefix.java new file mode 100644 index 0000000..fddc225 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XIpPrefix.java @@ -0,0 +1,57 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import biz.bokhorst.xprivacy.XHook; + +public class XIpPrefix extends XHook { + private Methods mMethod; + + private XIpPrefix(Methods method, String restrictionName) { + super(restrictionName, method.name(), "IpPrefix." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.net.IpPrefix"; + } + + // public InetAddress getAddress() + // public byte[] getRawAddress() + // https://developer.android.com/reference/android/net/IpPrefix.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/net/IpPrefix.java + + private enum Methods { + getAddress, getRawAddress + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XIpPrefix(Methods.getAddress, PrivacyManager.cInternet)); + listHook.add(new XIpPrefix(Methods.getRawAddress, PrivacyManager.cInternet)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getAddress: + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "InetAddress")); + break; + + case getRawAddress: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "IPInt")); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLinkProperties.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLinkProperties.java new file mode 100644 index 0000000..535e347 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLinkProperties.java @@ -0,0 +1,70 @@ +package biz.bokhorst.xprivacy; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +import android.net.LinkAddress; +import android.net.LinkProperties; + +public class XLinkProperties extends XHook { + private Methods mMethod; + + private XLinkProperties(Methods method, String restrictionName, String specifier) { + super(restrictionName, method.name(), "LinkProperties." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.net.LinkProperties"; + } + + // public List getAddresses() + // public List getAllAddresses() + // public List getAllLinkAddresses() + // public List getLinkAddresses() + // public @NonNull List getStackedLinks() + // http://developer.android.com/reference/android/net/LinkProperties.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/net/LinkProperties.java + + private enum Methods { + getAddresses, getAllAddresses, getAllLinkAddresses, getLinkAddresses, getStackedLinks + }; + + public static List getInstances() { + List listHook = new ArrayList(); + for (Methods addr : Methods.values()) + listHook.add(new XLinkProperties(addr, PrivacyManager.cInternet, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getAddresses: + case getAllAddresses: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getAllLinkAddresses: + case getLinkAddresses: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getStackedLinks: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + } + } +} \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLocationClient.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLocationClient.java new file mode 100644 index 0000000..80e6c6d --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLocationClient.java @@ -0,0 +1,152 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import android.app.PendingIntent; +import android.location.Location; +import android.os.Binder; +import android.util.Log; + +public class XLocationClient extends XHook { + private Methods mMethod; + private static final Map mMapProxy = new WeakHashMap(); + + private XLocationClient(Methods method, String restrictionName) { + super(restrictionName, method.name(), String.format("GMS.%s", method.name())); + mMethod = method; + } + + public String getClassName() { + return "com.google.android.gms.location.LocationClient"; + } + + // @formatter:off + + // void addGeofences(List geofences, PendingIntent pendingIntent, LocationClient.OnAddGeofencesResultListener listener) + // Location getLastLocation() + // void removeGeofences(List geofenceRequestIds, LocationClient.OnRemoveGeofencesResultListener listener) + // void removeGeofences(PendingIntent pendingIntent, LocationClient.OnRemoveGeofencesResultListener listener) + // void removeLocationUpdates(LocationListener listener) + // void removeLocationUpdates(PendingIntent callbackIntent) + // void requestLocationUpdates(LocationRequest request, PendingIntent callbackIntent) + // void requestLocationUpdates(LocationRequest request, LocationListener listener) + // void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) + // https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html + + // @formatter:on + + private enum Methods { + addGeofences, getLastLocation, removeGeofences, removeLocationUpdates, requestLocationUpdates + }; + + public static List getInstances() { + Util.log(null, Log.WARN, "Hooking LocationClient uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XLocationClient(Methods.addGeofences, PrivacyManager.cLocation)); + listHook.add(new XLocationClient(Methods.getLastLocation, PrivacyManager.cLocation)); + listHook.add(new XLocationClient(Methods.removeGeofences, null)); + listHook.add(new XLocationClient(Methods.removeLocationUpdates, null)); + listHook.add(new XLocationClient(Methods.requestLocationUpdates, PrivacyManager.cLocation)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case addGeofences: + if (isRestricted(param)) + param.setResult(null); + break; + + case getLastLocation: + // Do nothing + break; + + case removeGeofences: + if (isRestricted(param, PrivacyManager.cLocation, "GMS.addGeofences")) + param.setResult(null); + break; + + case removeLocationUpdates: + if (param.args.length > 0) + if (param.args[0] instanceof PendingIntent) { + if (isRestricted(param, PrivacyManager.cLocation, "GMS.requestLocationUpdates")) + param.setResult(null); + } else if (param.args[0] != null) + synchronized (mMapProxy) { + if (mMapProxy.containsKey(param.args[0])) + param.args[0] = mMapProxy.get(param.args[0]); + } + break; + + case requestLocationUpdates: + if (param.args.length > 1) + if (isRestricted(param)) + if (param.args[1] instanceof PendingIntent) + param.setResult(null); + else if (param.thisObject != null && param.args[1] != null) { + // Create proxy + ClassLoader cl = param.thisObject.getClass().getClassLoader(); + Class ll = Class.forName("com.google.android.gms.location.LocationListener", false, cl); + InvocationHandler ih = new OnLocationChangedHandler(Binder.getCallingUid(), param.args[1]); + Object proxy = Proxy.newProxyInstance(cl, new Class[] { ll }, ih); + + // Use proxy + synchronized (mMapProxy) { + mMapProxy.put(param.args[1], proxy); + } + param.args[1] = proxy; + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case addGeofences: + case removeGeofences: + // Do nothing + break; + + case getLastLocation: + Location location = (Location) param.getResult(); + if (location != null) + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); + break; + + case removeLocationUpdates: + // Do nothing + break; + + case requestLocationUpdates: + // Do nothing + break; + + } + } + + private class OnLocationChangedHandler implements InvocationHandler { + private int mUid; + private Object mTarget; + + public OnLocationChangedHandler(int uid, Object target) { + mUid = uid; + mTarget = target; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("onLocationChanged".equals(method.getName())) + args[0] = PrivacyManager.getDefacedLocation(mUid, (Location) args[0]); + return method.invoke(mTarget, args); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLocationManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLocationManager.java new file mode 100644 index 0000000..8e7a0d1 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XLocationManager.java @@ -0,0 +1,451 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import android.app.PendingIntent; +import android.location.Location; +import android.os.Binder; +import android.os.Bundle; +import android.os.IInterface; +import android.util.Log; +import android.location.GpsSatellite; +import android.location.GpsStatus; +import android.location.LocationListener; + +public class XLocationManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.location.LocationManager"; + private static final Map mMapProxy = new WeakHashMap(); + + private XLocationManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) + // public boolean addGpsStatusListener(GpsStatus.Listener listener) + // public boolean addNmeaListener(GpsStatus.NmeaListener listener) + // public void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) + // public List getAllProviders() + // public String getBestProvider(Criteria criteria, boolean enabledOnly) + // public GpsStatus getGpsStatus(GpsStatus status) + // public Location getLastKnownLocation(String provider) + // public List getProviders(boolean enabledOnly) + // public List getProviders(Criteria criteria, boolean enabledOnly) + // public boolean isProviderEnabled(String provider) + // public void removeUpdates(LocationListener listener) + // public void removeUpdates(PendingIntent intent) + // public void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) + // public void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener, Looper looper) + // public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, LocationListener listener, Looper looper) + // public void requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent) + // public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent) + // public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) + // public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) + // public void requestSingleUpdate(String provider, PendingIntent intent) + // public void requestSingleUpdate(Criteria criteria, PendingIntent intent) + // public boolean sendExtraCommand(String provider, String command, Bundle extras) + // frameworks/base/location/java/android/location/LocationManager.java + // http://developer.android.com/reference/android/location/LocationManager.html + + // public void requestLocationUpdates(LocationRequest request, ILocationListener listener, android.app.PendingIntent intent, java.lang.String packageName) + // public void removeUpdates(ILocationListener listener, android.app.PendingIntent intent, java.lang.String packageName) + // public void requestGeofence(LocationRequest request, Geofence geofence, android.app.PendingIntent intent, java.lang.String packageName) + // public void removeGeofence(Geofence fence, android.app.PendingIntent intent, java.lang.String packageName) + // public Location getLastLocation(LocationRequest request, java.lang.String packageName) + // public boolean addGpsStatusListener(IGpsStatusListener listener, java.lang.String packageName) + // public void removeGpsStatusListener(IGpsStatusListener listener) + // public java.util.List getAllProviders() + // public java.util.List getProviders(Criteria criteria, boolean enabledOnly) + // public java.lang.String getBestProvider(Criteria criteria, boolean enabledOnly) + // public boolean isProviderEnabled(java.lang.String provider) + // public boolean sendExtraCommand(java.lang.String provider, java.lang.String command, android.os.Bundle extras) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/com/android/server/LocationManagerService.java + // public boolean addGpsMeasurementsListener(IGpsMeasurementsListener listener, String packageName) + // public boolean addGpsNavigationMessageListener(IGpsNavigationMessageListener listener, String packageName) + // public boolean removeGpsMeasurementsListener(IGpsMeasurementsListener listener) + // public boolean removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/LocationManagerService.java + + // @formatter:on + + // @formatter:off + private enum Methods { + addGeofence, addGpsStatusListener, addNmeaListener, addProximityAlert, + getAllProviders, getBestProvider, getProviders, isProviderEnabled, + getGpsStatus, + getLastKnownLocation, + removeUpdates, + requestLocationUpdates, requestSingleUpdate, + sendExtraCommand, + + Srv_requestLocationUpdates, Srv_removeUpdates, + Srv_requestGeofence, Srv_removeGeofence, + Srv_getLastLocation, + Srv_addGpsStatusListener, Srv_removeGpsStatusListener, + Srv_getAllProviders, Srv_getProviders, Srv_getBestProvider, Srv_isProviderEnabled, + Srv_sendExtraCommand, + + Srv_addGpsMeasurementsListener, Srv_addGpsNavigationMessageListener, Srv_removeGpsMeasurementsListener, Srv_removeGpsNavigationMessageListener + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + for (Methods loc : Methods.values()) + if (loc == Methods.removeUpdates) + listHook.add(new XLocationManager(loc, null, className)); + else if (loc.name().startsWith("Srv_remove")) { + if (server) + listHook.add(new XLocationManager(loc, null, "com.android.server.LocationManagerService")); + } else if (loc.name().startsWith("Srv_")) { + if (server) + listHook.add(new XLocationManager(loc, PrivacyManager.cLocation, + "com.android.server.LocationManagerService")); + } else + listHook.add(new XLocationManager(loc, PrivacyManager.cLocation, className)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case addGeofence: + case addProximityAlert: + case Srv_requestGeofence: + if (isRestricted(param)) + param.setResult(null); + break; + + case Srv_removeGeofence: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_requestGeofence")) + param.setResult(null); + break; + + case addGpsStatusListener: + case addNmeaListener: + case Srv_addGpsStatusListener: + case Srv_addGpsMeasurementsListener: + case Srv_addGpsNavigationMessageListener: + if (isRestricted(param)) + param.setResult(false); + break; + + case Srv_removeGpsStatusListener: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_addGpsStatusListener")) + param.setResult(null); + break; + + case Srv_removeGpsMeasurementsListener: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_addGpsMeasurementsListener")) + param.setResult(null); + break; + + case Srv_removeGpsNavigationMessageListener: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_addGpsNavigationMessageListener")) + param.setResult(null); + break; + + case getAllProviders: + case getBestProvider: + case getGpsStatus: + case getLastKnownLocation: + case getProviders: + case isProviderEnabled: + case Srv_getAllProviders: + case Srv_getProviders: + case Srv_getBestProvider: + case Srv_isProviderEnabled: + case Srv_getLastLocation: + // Do nothing + break; + + case removeUpdates: + if (isRestricted(param, PrivacyManager.cLocation, "requestLocationUpdates")) + unproxyLocationListener(param, 0, true); + break; + + case requestLocationUpdates: + if (param.args.length > 0 && param.args[0] instanceof String) { + if (isRestrictedExtra(param, (String) param.args[0])) + proxyLocationListener(param, 3, LocationListener.class, true); + } else { + if (isRestricted(param)) + proxyLocationListener(param, 3, LocationListener.class, true); + } + break; + + case Srv_removeUpdates: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_requestLocationUpdates")) + if (param.args.length > 1) + if (param.args[0] != null) // ILocationListener + unproxyLocationListener(param, 0, false); + else if (param.args[1] != null) // PendingIntent + param.setResult(null); + break; + + case Srv_requestLocationUpdates: + if (isRestricted(param)) + if (param.args.length > 2) + if (param.args[1] != null) // ILocationListener + proxyLocationListener(param, 1, Class.forName("android.location.ILocationListener"), false); + else if (param.args[2] != null) // PendingIntent + param.setResult(null); + break; + + case requestSingleUpdate: + if (param.args.length > 0 && param.args[0] instanceof String) { + if (isRestrictedExtra(param, (String) param.args[0])) + proxyLocationListener(param, 1, LocationListener.class, true); + } else { + if (isRestricted(param)) + proxyLocationListener(param, 1, LocationListener.class, true); + } + break; + + case sendExtraCommand: + case Srv_sendExtraCommand: + // Do nothing + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case addGeofence: + case addNmeaListener: + case addGpsStatusListener: + case addProximityAlert: + case Srv_requestGeofence: + case Srv_addGpsStatusListener: + case Srv_addGpsMeasurementsListener: + case Srv_addGpsNavigationMessageListener: + case Srv_removeGeofence: + case Srv_removeGpsStatusListener: + case Srv_removeGpsMeasurementsListener: + case Srv_removeGpsNavigationMessageListener: + // Do nothing + break; + + case isProviderEnabled: + case Srv_isProviderEnabled: + if (param.args.length > 0) { + String provider = (String) param.args[0]; + if (isRestrictedExtra(param, provider)) + param.setResult(false); + } + break; + + case getGpsStatus: + if (param.getResult() instanceof GpsStatus) + if (isRestricted(param)) { + GpsStatus status = (GpsStatus) param.getResult(); + // private GpsSatellite mSatellites[] + try { + Field mSatellites = status.getClass().getDeclaredField("mSatellites"); + mSatellites.setAccessible(true); + mSatellites.set(status, new GpsSatellite[0]); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + break; + + case getProviders: + case getAllProviders: + case Srv_getAllProviders: + case Srv_getProviders: + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getBestProvider: + case Srv_getBestProvider: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(null); + break; + + case getLastKnownLocation: + if (param.args.length > 0 && param.getResult() instanceof Location) { + String provider = (String) param.args[0]; + Location location = (Location) param.getResult(); + if (isRestrictedExtra(param, provider)) + param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); + } + break; + + case Srv_getLastLocation: + if (param.getResult() instanceof Location) { + Location location = (Location) param.getResult(); + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedLocation(Binder.getCallingUid(), location)); + } + break; + + case removeUpdates: + case requestLocationUpdates: + case requestSingleUpdate: + case Srv_removeUpdates: + case Srv_requestLocationUpdates: + // Do nothing + break; + + case sendExtraCommand: + case Srv_sendExtraCommand: + if (param.args.length > 0) { + String provider = (String) param.args[0]; + if (isRestrictedExtra(param, provider)) + param.setResult(false); + } + break; + } + } + + private void proxyLocationListener(XParam param, int arg, Class interfaze, boolean client) throws Throwable { + if (param.args.length > arg) + if (param.args[arg] instanceof PendingIntent) + param.setResult(null); + + else if (param.args[arg] != null && param.thisObject != null) { + if (client) { + Object key = param.args[arg]; + synchronized (mMapProxy) { + // Reuse existing proxy + if (mMapProxy.containsKey(key)) { + Util.log(this, Log.INFO, "Reuse existing proxy uid=" + Binder.getCallingUid()); + param.args[arg] = mMapProxy.get(key); + return; + } + + // Already proxied + if (mMapProxy.containsValue(key)) { + Util.log(this, Log.INFO, "Already proxied uid=" + Binder.getCallingUid()); + return; + } + } + + // Create proxy + Util.log(this, Log.INFO, "Creating proxy uid=" + Binder.getCallingUid()); + Object proxy = new ProxyLocationListener(Binder.getCallingUid(), (LocationListener) param.args[arg]); + + // Use proxy + synchronized (mMapProxy) { + mMapProxy.put(key, proxy); + } + param.args[arg] = proxy; + } else { + // Create proxy + ClassLoader cl = param.thisObject.getClass().getClassLoader(); + InvocationHandler ih = new OnLocationChangedHandler(Binder.getCallingUid(), param.args[arg]); + Object proxy = Proxy.newProxyInstance(cl, new Class[] { interfaze }, ih); + + Object key = param.args[arg]; + if (key instanceof IInterface) + key = ((IInterface) key).asBinder(); + + // Use proxy + synchronized (mMapProxy) { + mMapProxy.put(key, proxy); + } + param.args[arg] = proxy; + } + } + } + + private void unproxyLocationListener(XParam param, int arg, boolean client) { + if (param.args.length > arg) + if (param.args[arg] instanceof PendingIntent) + param.setResult(null); + + else if (param.args[arg] != null) { + if (client) { + Object key = param.args[arg]; + synchronized (mMapProxy) { + if (mMapProxy.containsKey(key)) { + Util.log(this, Log.INFO, "Removing proxy uid=" + Binder.getCallingUid()); + param.args[arg] = mMapProxy.get(key); + } + } + } else { + Object key = param.args[arg]; + if (key instanceof IInterface) + key = ((IInterface) key).asBinder(); + + synchronized (mMapProxy) { + if (mMapProxy.containsKey(key)) + param.args[arg] = mMapProxy.get(key); + } + } + } + } + + private static class ProxyLocationListener implements LocationListener { + private int mUid; + private LocationListener mListener; + + public ProxyLocationListener(int uid, LocationListener listener) { + mUid = uid; + mListener = listener; + } + + @Override + public void onLocationChanged(Location location) { + Util.log(null, Log.INFO, "Location changed uid=" + Binder.getCallingUid()); + Location fakeLocation = PrivacyManager.getDefacedLocation(mUid, location); + mListener.onLocationChanged(fakeLocation); + } + + @Override + public void onProviderDisabled(String provider) { + mListener.onProviderDisabled(provider); + } + + @Override + public void onProviderEnabled(String provider) { + mListener.onProviderEnabled(provider); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + mListener.onStatusChanged(provider, status, extras); + } + } + + private class OnLocationChangedHandler implements InvocationHandler { + private int mUid; + private Object mTarget; + + public OnLocationChangedHandler(int uid, Object target) { + mUid = uid; + mTarget = target; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("onLocationChanged".equals(method.getName())) + args[0] = PrivacyManager.getDefacedLocation(mUid, (Location) args[0]); + return method.invoke(mTarget, args); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XMediaRecorder.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XMediaRecorder.java new file mode 100644 index 0000000..51a7a15 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XMediaRecorder.java @@ -0,0 +1,60 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +public class XMediaRecorder extends XHook { + private Methods mMethod; + + private XMediaRecorder(Methods method, String restrictionName) { + super(restrictionName, method.name(), "MediaRecorder." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.media.MediaRecorder"; + } + + // void setOutputFile(FileDescriptor fd) + // void setOutputFile(String path) + // public prepare() + // public native void start() + // void stop() + // frameworks/base/media/java/android/media/MediaRecorder.java + // http://developer.android.com/reference/android/media/MediaRecorder.html + + private enum Methods { + setOutputFile, prepare, start, stop + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XMediaRecorder(Methods.setOutputFile, PrivacyManager.cMedia)); + listHook.add(new XMediaRecorder(Methods.prepare, null)); + listHook.add(new XMediaRecorder(Methods.start, PrivacyManager.cMedia)); + listHook.add(new XMediaRecorder(Methods.stop, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case setOutputFile: + case start: + if (isRestricted(param)) + param.setResult(null); + break; + + case prepare: + case stop: + if (isRestricted(param, PrivacyManager.cMedia, "MediaRecorder.start")) + param.setResult(null); + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNetworkInfo.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNetworkInfo.java new file mode 100644 index 0000000..61b712d --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNetworkInfo.java @@ -0,0 +1,69 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.net.NetworkInfo; +import android.os.Binder; +import android.util.Log; + +public class XNetworkInfo extends XHook { + private Methods mMethod; + + private XNetworkInfo(Methods method, String restrictionName) { + super(restrictionName, method.name(), "NetworkInfo." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.net.NetworkInfo"; + } + + // public DetailedState getDetailedState() + // public State getState() + // public boolean isConnected() + // public boolean isConnectedOrConnecting() + // frameworks/base/core/java/android/net/NetworkInfo.java + // http://developer.android.com/reference/android/net/NetworkInfo.html + + private enum Methods { + getDetailedState, getExtraInfo, getState, isConnected, isConnectedOrConnecting + }; + + public static List getInstances() { + List listHook = new ArrayList(); + for (Methods ninfo : Methods.values()) + if (ninfo == Methods.getExtraInfo) + listHook.add(new XNetworkInfo(ninfo, PrivacyManager.cNetwork)); + else + listHook.add(new XNetworkInfo(ninfo, PrivacyManager.cInternet)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.getDetailedState) { + if (param.getResult() != null && isRestricted(param)) + param.setResult(NetworkInfo.DetailedState.DISCONNECTED); + + } else if (mMethod == Methods.getExtraInfo) { + if (param.getResult() != null && isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "ExtraInfo")); + + } else if (mMethod == Methods.getState) { + if (param.getResult() != null && isRestricted(param)) + param.setResult(NetworkInfo.State.DISCONNECTED); + + } else if (mMethod == Methods.isConnected || mMethod == Methods.isConnectedOrConnecting) { + if (isRestricted(param)) + param.setResult(false); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNetworkInterface.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNetworkInterface.java new file mode 100644 index 0000000..b0ef1cb --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNetworkInterface.java @@ -0,0 +1,132 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XNetworkInterface extends XHook { + private Methods mMethod; + + private XNetworkInterface(Methods method, String restrictionName) { + super(restrictionName, method.name(), "NetworkInterface." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "java.net.NetworkInterface"; + } + + // Internet: + // - public static NetworkInterface getByIndex(int index) + // - public static NetworkInterface getByInetAddress(InetAddress address) + // - public static NetworkInterface getByName(String interfaceName) + // - public static Enumeration getNetworkInterfaces() + + // Network: + // - public byte[] getHardwareAddress() + // - public Enumeration getInetAddresses() + // - public List getInterfaceAddresses() + + // libcore/luni/src/main/java/java/net/NetworkInterface.java + // http://developer.android.com/reference/java/net/NetworkInterface.html + + // @formatter:off + private enum Methods { + getByIndex, getByInetAddress, getByName, getNetworkInterfaces, + getHardwareAddress, getInetAddresses, getInterfaceAddresses + }; + // @formatter:on + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XNetworkInterface(Methods.getByIndex, PrivacyManager.cInternet)); + listHook.add(new XNetworkInterface(Methods.getByInetAddress, PrivacyManager.cInternet)); + listHook.add(new XNetworkInterface(Methods.getByName, PrivacyManager.cInternet)); + listHook.add(new XNetworkInterface(Methods.getNetworkInterfaces, PrivacyManager.cInternet)); + + listHook.add(new XNetworkInterface(Methods.getHardwareAddress, PrivacyManager.cNetwork)); + listHook.add(new XNetworkInterface(Methods.getInetAddresses, PrivacyManager.cNetwork)); + listHook.add(new XNetworkInterface(Methods.getInterfaceAddresses, PrivacyManager.cNetwork)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + if (getRestrictionName().equals(PrivacyManager.cInternet)) { + // Internet: fake offline state + if (mMethod == Methods.getByIndex || mMethod == Methods.getByInetAddress || mMethod == Methods.getByName + || mMethod == Methods.getNetworkInterfaces) { + if (param.getResult() != null && isRestricted(param)) + param.setResult(null); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + + } else if (getRestrictionName().equals(PrivacyManager.cNetwork)) { + // Network + NetworkInterface ni = (NetworkInterface) param.thisObject; + if (ni != null) + if (param.getResult() != null && isRestricted(param)) + if (mMethod == Methods.getHardwareAddress) { + String mac = (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "MAC"); + long lMac = Long.parseLong(mac.replace(":", ""), 16); + byte[] address = ByteBuffer.allocate(8).putLong(lMac).array(); + param.setResult(address); + + } else if (mMethod == Methods.getInetAddresses) { + @SuppressWarnings("unchecked") + Enumeration addresses = (Enumeration) param.getResult(); + List listAddress = new ArrayList(); + for (InetAddress address : Collections.list(addresses)) + if (address.isAnyLocalAddress() || address.isLoopbackAddress()) + listAddress.add(address); + else + listAddress.add((InetAddress) PrivacyManager.getDefacedProp(Binder.getCallingUid(), + "InetAddress")); + param.setResult(Collections.enumeration(listAddress)); + + } else if (mMethod == Methods.getInterfaceAddresses) { + @SuppressWarnings("unchecked") + List listAddress = (List) param.getResult(); + for (InterfaceAddress address : listAddress) { + // address + try { + Field fieldAddress = InterfaceAddress.class.getDeclaredField("address"); + fieldAddress.setAccessible(true); + fieldAddress.set(address, + PrivacyManager.getDefacedProp(Binder.getCallingUid(), "InetAddress")); + } catch (Throwable ex) { + Util.bug(this, ex); + } + + // broadcastAddress + try { + Field fieldBroadcastAddress = InterfaceAddress.class + .getDeclaredField("broadcastAddress"); + fieldBroadcastAddress.setAccessible(true); + fieldBroadcastAddress.set(address, + PrivacyManager.getDefacedProp(Binder.getCallingUid(), "InetAddress")); + } catch (Throwable ex) { + Util.bug(this, ex); + } + } + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNfcAdapter.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNfcAdapter.java new file mode 100644 index 0000000..2f3f34a --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XNfcAdapter.java @@ -0,0 +1,55 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +public class XNfcAdapter extends XHook { + private Methods mMethod; + + protected XNfcAdapter(Methods method, String restrictionName) { + super(restrictionName, method.name(), null); + mMethod = method; + } + + @Override + public String getClassName() { + return "android.nfc.NfcAdapter"; + } + + private enum Methods { + getDefaultAdapter, getNfcAdapter + }; + + // public static NfcAdapter getDefaultAdapter() [deprecated] + // public static NfcAdapter getDefaultAdapter(Context context) + // public static synchronized NfcAdapter getNfcAdapter(Context context) + // frameworks/base/core/java/android/nfc/NfcAdapter.java + // http://developer.android.com/reference/android/nfc/NfcAdapter.html + + // NfcManager.getDefaultAdapter calls NfcAdapter.getNfcAdapter + // http://developer.android.com/reference/android/nfc/NfcManager.html + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XNfcAdapter(Methods.getDefaultAdapter, PrivacyManager.cNfc)); + listHook.add(new XNfcAdapter(Methods.getNfcAdapter, PrivacyManager.cNfc)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.getDefaultAdapter || mMethod == Methods.getNfcAdapter) { + if (isRestricted(param)) + param.setResult(null); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPackageManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPackageManager.java new file mode 100644 index 0000000..39d8926 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPackageManager.java @@ -0,0 +1,405 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; +import android.content.ComponentName; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; + +public class XPackageManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.app.ApplicationPackageManager"; + + private XPackageManager(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + mClassName = "com.android.server.pm.PackageManagerService"; + } + + private XPackageManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public List getInstalledApplications(int flags) + // public List getInstalledPackages(int flags) + // public String[] getPackagesForUid(int uid) + // public List getPackagesHoldingPermissions(String[] permissions, int flags) + // public List getPreferredPackages(int flags) + // public List queryBroadcastReceivers(Intent intent, int flags) + // public List queryContentProviders(String processName, int uid, int flags) + // public List queryIntentActivities(Intent intent, int flags) + // public List queryIntentActivityOptions(ComponentName caller, Intent[] specifics, Intent intent, int flags) + // public List queryIntentContentProviders(Intent intent, int flags) + // public List queryIntentServices(Intent intent, int flags) + // frameworks/base/core/java/android/app/ApplicationPackageManager.java + // http://developer.android.com/reference/android/content/pm/PackageManager.html + + // public int checkPermission(String permName, String pkgName) + // public int checkUidPermission(String permName, int uid) + + // public java.lang.String[] getPackagesForUid(int uid) + // public java.util.List queryIntentActivities(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) + // public java.util.List queryIntentActivityOptions(android.content.ComponentName caller, android.content.Intent[] specifics, java.lang.String[] specificTypes, android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) + // public java.util.List queryIntentReceivers(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) + // public java.util.List queryIntentServices(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) + // public java.util.List queryIntentContentProviders(android.content.Intent intent, java.lang.String resolvedType, int flags, int userId) + // public java.util.List getPersistentApplications(int flags) + // public java.util.List queryContentProviders(java.lang.String processName, int uid, int flags) + // public java.util.List getPreferredPackages(int flags) + // public android.content.pm.ParceledListSlice getInstalledPackages(int flags, int userId) + // public android.content.pm.ParceledListSlice getPackagesHoldingPermissions(java.lang.String[] permissions, int flags, int userId) + // public android.content.pm.ParceledListSlice getInstalledApplications(int flags, int userId) + + // public PackageInfo getPackageInfo(String packageName, int flags, int userId) + // public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) + + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/pm/PackageManagerService.java + + // @formatter:on + + // @formatter:off + private enum Methods { + getInstalledApplications, getInstalledPackages, + getPackagesForUid, + getPackagesHoldingPermissions, + getPreferredActivities, getPreferredPackages, + queryBroadcastReceivers, queryContentProviders, + queryIntentActivities, queryIntentActivityOptions, + queryIntentContentProviders, queryIntentServices, + + checkPermission, checkUidPermission, + + Srv_getPackageInfo, Srv_getApplicationInfo, + Srv_getInstalledApplications, Srv_getInstalledPackages, + Srv_getPackagesForUid, + Srv_getPackagesHoldingPermissions, + Srv_getPersistentApplications, + Srv_getPreferredPackages, + Srv_queryContentProviders, + Srv_queryIntentActivities, Srv_queryIntentActivityOptions, + Srv_queryIntentContentProviders, + Srv_queryIntentReceivers, + Srv_queryIntentServices + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + if (server) { + listHook.add(new XPackageManager(Methods.Srv_getPackageInfo, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getApplicationInfo, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getInstalledApplications, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getInstalledPackages, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getPackagesForUid, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getPackagesHoldingPermissions, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getPersistentApplications, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_getPreferredPackages, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_queryContentProviders, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_queryIntentActivities, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_queryIntentActivityOptions, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_queryIntentContentProviders, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_queryIntentReceivers, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.Srv_queryIntentServices, PrivacyManager.cSystem)); + + listHook.add(new XPackageManager(Methods.checkPermission, PrivacyManager.cSystem)); + listHook.add(new XPackageManager(Methods.checkUidPermission, PrivacyManager.cSystem)); + } else { + listHook.add(new XPackageManager(Methods.getInstalledApplications, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.getInstalledPackages, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.getPackagesForUid, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.getPackagesHoldingPermissions, PrivacyManager.cSystem, + className)); + listHook.add(new XPackageManager(Methods.getPreferredActivities, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.getPreferredPackages, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.queryBroadcastReceivers, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.queryContentProviders, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.queryIntentActivities, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.queryIntentActivityOptions, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.queryIntentContentProviders, PrivacyManager.cSystem, className)); + listHook.add(new XPackageManager(Methods.queryIntentServices, PrivacyManager.cSystem, className)); + } + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.getPreferredActivities) + if (param.args.length > 1) + if (isRestricted(param)) { + param.args[0] = new ArrayList(); + param.args[1] = new ArrayList(); + param.setResult(0); + } + } + + @Override + @SuppressWarnings("unchecked") + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case Srv_getPackageInfo: + case Srv_getApplicationInfo: + if (param.args.length > 0 && param.args[0] instanceof String && param.getResult() != null) { + // Allow own package name + int uid = -1; + if (mMethod == Methods.Srv_getPackageInfo) { + PackageInfo pInfo = (PackageInfo) param.getResult(); + if (pInfo.applicationInfo != null) + uid = pInfo.applicationInfo.uid; + } else if (mMethod == Methods.Srv_getApplicationInfo) { + ApplicationInfo aInfo = (ApplicationInfo) param.getResult(); + uid = aInfo.uid; + } + if (uid == Binder.getCallingUid()) + return; + + // Prevent recursion + String packageName = (String) param.args[0]; + if (!XPackageManager.class.getPackage().getName().equals(packageName)) + if (isRestrictedExtra(param, packageName)) + if (!isPackageAllowed(uid, packageName)) + param.setResult(null); + } + break; + + case Srv_getInstalledApplications: + if (param.getResult() != null) + if (isRestricted(param)) { + Method mGetList = param.getResult().getClass().getDeclaredMethod("getList"); + List listAppInfo = (List) mGetList.invoke(param.getResult()); + Constructor constructor = param.getResult().getClass().getConstructor(List.class); + param.setResult(constructor.newInstance(filterApplicationInfo(listAppInfo))); + } + break; + + case Srv_getInstalledPackages: + case Srv_getPackagesHoldingPermissions: + if (param.getResult() != null) + if (isRestricted(param)) { + Method mGetList = param.getResult().getClass().getDeclaredMethod("getList"); + List listPkgInfo = (List) mGetList.invoke(param.getResult()); + Constructor constructor = param.getResult().getClass().getConstructor(List.class); + param.setResult(constructor.newInstance(filterPackageInfo(listPkgInfo))); + } + break; + + case getPackagesForUid: + case Srv_getPackagesForUid: + if (param.args.length > 0 && param.args[0] instanceof Integer && param.getResult() != null) { + int uid = (Integer) param.args[0]; + if (uid != Binder.getCallingUid()) + if (isRestrictedExtra(param, Integer.toString(uid))) { + List lstResult = new ArrayList(); + if (param.getResult() instanceof String[]) + for (String packageName : (String[]) param.getResult()) + if (isPackageAllowed(Binder.getCallingUid(), packageName)) + lstResult.add(packageName); + if (lstResult.size() == 0) + param.setResult(null); + else + param.setResult(lstResult.toArray(new String[0])); + } + } + break; + + case Srv_getPersistentApplications: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(filterApplicationInfo((List) param.getResult())); + break; + + case Srv_getPreferredPackages: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(filterPackageInfo((List) param.getResult())); + break; + + case Srv_queryIntentActivities: + case Srv_queryIntentActivityOptions: + case Srv_queryIntentContentProviders: + case Srv_queryIntentReceivers: + case Srv_queryIntentServices: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(filterResolveInfo((List) param.getResult())); + break; + + case getInstalledApplications: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(filterApplicationInfo((List) param.getResult())); + break; + + case getPreferredActivities: + break; + + case getInstalledPackages: + case getPackagesHoldingPermissions: + case getPreferredPackages: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(filterPackageInfo((List) param.getResult())); + break; + + case queryBroadcastReceivers: + case queryIntentActivities: + case queryIntentActivityOptions: + case queryIntentContentProviders: + case queryIntentServices: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(filterResolveInfo((List) param.getResult())); + break; + + case queryContentProviders: + case Srv_queryContentProviders: + if (param.args.length > 1 && param.args[1] instanceof Integer && param.getResult() != null) { + int uid = (Integer) param.args[1]; + if (uid != Binder.getCallingUid()) { + String processName = (String) param.args[0]; + if (isRestrictedExtra(param, processName)) + param.setResult(filterProviderInfo((List) param.getResult())); + } + } + break; + + case checkPermission: + if (param.args.length > 1 && param.args[0] instanceof String && param.args[1] instanceof String) { + String permName = (String) param.args[0]; + String pkgName = (String) param.args[1]; + int resultOfCheck = (Integer) param.getResult(); + + if (resultOfCheck != PackageManager.PERMISSION_GRANTED) + return; + + // Get uid + int uid; + Class clazz = param.thisObject.getClass(); + try { + // public int getPackageUid(String packageName, int userId) + Method mGetPackageUid = clazz.getDeclaredMethod("getPackageUid", String.class, int.class); + mGetPackageUid.setAccessible(true); + int userId = Util.getUserId(Binder.getCallingUid()); + uid = (Integer) mGetPackageUid.invoke(param.thisObject, pkgName, userId); + } catch (NoSuchMethodException ignored) { + // public int getPackageUid(String packageName) + Method mGetPackageUid = clazz.getDeclaredMethod("getPackageUid", String.class); + mGetPackageUid.setAccessible(true); + uid = (Integer) mGetPackageUid.invoke(param.thisObject, pkgName); + } + + checkPermission(param, uid, permName); + } + break; + + case checkUidPermission: + if (param.args.length > 1 && param.args[0] instanceof String && param.args[1] instanceof Integer) { + String permName = (String) param.args[0]; + int uid = (Integer) param.args[1]; + int resultOfCheck = (Integer) param.getResult(); + + if (resultOfCheck == PackageManager.PERMISSION_GRANTED) + checkPermission(param, uid, permName); + } + break; + + } + } + + private void checkPermission(XParam param, int uid, String permName) throws Throwable { + if ("android.permission.CAMERA".endsWith(permName)) + if (getRestricted(uid, PrivacyManager.cMedia, "Camera.permission")) + param.setResult(PackageManager.PERMISSION_DENIED); + + if ("android.permission.RECORD_AUDIO".endsWith(permName)) + if (getRestricted(uid, PrivacyManager.cMedia, "Record.Audio.permission")) + param.setResult(PackageManager.PERMISSION_DENIED); + + if ("android.permission.RECORD_VIDEO".endsWith(permName)) + if (getRestricted(uid, PrivacyManager.cMedia, "Record.Video.permission")) + param.setResult(PackageManager.PERMISSION_DENIED); + + if (PrivacyManager.getSettingBool(0, PrivacyManager.cSettingPermMan, false)) { + permName = permName.replace("android.permission.", ""); + if (isRestrictedExtra(uid, getRestrictionName(), getMethodName(), permName)) + param.setResult(PackageManager.PERMISSION_DENIED); + } + } + + private List filterApplicationInfo(List original) { + ArrayList result = new ArrayList(); + for (ApplicationInfo appInfo : original) + if (isPackageAllowed(appInfo.uid, appInfo.packageName)) + result.add(appInfo); + return result; + } + + private List filterPackageInfo(List original) { + ArrayList result = new ArrayList(); + for (PackageInfo pkgInfo : original) + if (isPackageAllowed(pkgInfo.applicationInfo == null ? 0 : pkgInfo.applicationInfo.uid, pkgInfo.packageName)) + result.add(pkgInfo); + return result; + } + + private List filterProviderInfo(List original) { + ArrayList result = new ArrayList(); + for (ProviderInfo provInfo : original) + if (isPackageAllowed(provInfo.applicationInfo == null ? 0 : provInfo.applicationInfo.uid, + provInfo.packageName)) + result.add(provInfo); + return result; + } + + private List filterResolveInfo(List original) { + ArrayList result = new ArrayList(); + for (ResolveInfo resInfo : original) + if (resInfo.activityInfo != null && resInfo.activityInfo.applicationInfo != null) + if (isPackageAllowed(resInfo.activityInfo.applicationInfo.uid, + resInfo.activityInfo.applicationInfo.packageName)) + result.add(resInfo); + return result; + } + + public static boolean isPackageAllowed(int puid, String packageName) { + int uid = Binder.getCallingUid(); + if (puid == uid) + return true; + + if (packageName == null) { + Util.log(null, Log.WARN, "isPackageAllowed uid=" + uid + " package=" + packageName); + Util.logStack(null, Log.WARN); + return false; + } + + boolean allowed = PrivacyManager.getSettingBool(-uid, Meta.cTypeApplication, packageName, false); + boolean blacklist = PrivacyManager.getSettingBool(-uid, PrivacyManager.cSettingBlacklist, false); + if (blacklist) + allowed = !allowed; + if (allowed) + Util.log(null, Log.INFO, "Allowing package=" + packageName); + return allowed; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XParam.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XParam.java new file mode 100644 index 0000000..008d57c --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XParam.java @@ -0,0 +1,102 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import android.util.Log; + +import de.robv.android.xposed.XC_MethodHook.MethodHookParam; + +public class XParam { + public Member method; + public Object thisObject; + public Object[] args; + private Object mResult; + private Throwable mThrowable; + private boolean mHasResult = false; + private boolean mHasThrowable = false; + private Map mExtra = null; + + private XParam() { + } + + public static XParam fromXposed(MethodHookParam param) { + XParam xparam = new XParam(); + xparam.method = param.method; + xparam.thisObject = param.thisObject; + xparam.args = param.args; + xparam.mResult = param.getResult(); + xparam.mThrowable = param.getThrowable(); + + if (xparam.args == null) + xparam.args = new Object[] {}; + + return xparam; + } + + public boolean doesReturn(Class result) { + if (this.method instanceof Method) + return (((Method) this.method).getReturnType().equals(result)); + return false; + } + + public void setResult(Object result) { + if (result instanceof Throwable) { + Util.log(null, Log.ERROR, "Set result throwable=" + result); + setThrowable((Throwable) result); + } else { + mResult = result; + mHasResult = true; + } + } + + public boolean hasResult() { + return mHasResult; + } + + public Object getResult() { + return mResult; + } + + public boolean doesThrow(Class ex) { + if (this.method instanceof Method) + for (Class t : ((Method) this.method).getExceptionTypes()) + if (t.equals(ex)) + return true; + return false; + } + + public void setThrowable(Throwable ex) { + mThrowable = ex; + mHasThrowable = true; + } + + public boolean hasThrowable() { + return mHasThrowable; + } + + public Throwable getThrowable() { + return mThrowable; + } + + public Object getExtras() { + return mExtra; + } + + @SuppressWarnings("unchecked") + public void setExtras(Object extra) { + mExtra = (Map) extra; + } + + public void setObjectExtra(String name, Object value) { + if (mExtra == null) + mExtra = new HashMap(); + mExtra.put(name, value); + } + + public Object getObjectExtra(String name) { + return (mExtra == null ? null : mExtra.get(name)); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPlaceDetectionApi.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPlaceDetectionApi.java new file mode 100644 index 0000000..518909a --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPlaceDetectionApi.java @@ -0,0 +1,57 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XPlaceDetectionApi extends XHook { + private Methods mMethod; + private String mClassName; + + private XPlaceDetectionApi(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), "GMS5." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // abstract PendingResult getCurrentPlace(GoogleApiClient client, PlaceFilter filter) + // https://developer.android.com/reference/com/google/android/gms/location/places/PlaceDetectionApi.html + + // @formatter:on + + private enum Methods { + getCurrentPlace + }; + + public static List getInstances(Object instance) { + String className = instance.getClass().getName(); + Util.log(null, Log.WARN, "Hooking PlaceDetectionApi class=" + className + " uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XPlaceDetectionApi(Methods.getCurrentPlace, PrivacyManager.cLocation, className)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getCurrentPlace: + if (isRestricted(param)) + param.setResult(XGoogleApiClient.getPendingResult(param.thisObject.getClass().getClassLoader())); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPrivacy.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPrivacy.java new file mode 100644 index 0000000..ba4faf5 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XPrivacy.java @@ -0,0 +1,676 @@ +package biz.bokhorst.xprivacy; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import android.content.Context; +import android.os.Build; +import android.os.Process; +import android.util.Log; +import de.robv.android.xposed.IXposedHookZygoteInit; +import de.robv.android.xposed.IXposedHookLoadPackage; +import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XC_MethodHook; +import static de.robv.android.xposed.XposedHelpers.findClass; + +public class XPrivacy implements IXposedHookLoadPackage, IXposedHookZygoteInit { + private static String mSecret = null; + private static List mListHookError = new ArrayList(); + private static List mListDisabled = new ArrayList(); + + public void initZygote(StartupParam startupParam) throws Throwable { + Util.log(null, Log.WARN, "Init path=" + startupParam.modulePath); + + // Check for LBE security master + if (Util.hasLBE()) { + Util.log(null, Log.ERROR, "LBE installed"); + return; + } + + // Generate secret + mSecret = Long.toHexString(new Random().nextLong()); + + // Reading files with SELinux enabled can result in bootloops + boolean selinux = Util.isSELinuxEnforced(); + if ("true".equals(Util.getXOption("ignoreselinux"))) { + selinux = false; + Log.w("Xprivacy", "Ignoring SELinux"); + } + + // Read list of disabled hooks + if (mListDisabled.size() == 0 && !selinux) { + File disabled = new File("/data/system/xprivacy/disabled"); + if (disabled.exists() && disabled.canRead()) + try { + Log.w("XPrivacy", "Reading " + disabled.getAbsolutePath()); + FileInputStream fis = new FileInputStream(disabled); + InputStreamReader ir = new InputStreamReader(fis); + BufferedReader br = new BufferedReader(ir); + String line; + while ((line = br.readLine()) != null) + if (line.length() > 0 && !line.startsWith("#")) { + String[] name = line.split("/"); + if (name.length > 0) { + String methodName = (name.length > 1 ? name[1] : null); + CRestriction restriction = new CRestriction(0, name[0], methodName, null); + Log.w("XPrivacy", "Disabling " + restriction); + mListDisabled.add(restriction); + } + } + br.close(); + ir.close(); + fis.close(); + } catch (Throwable ex) { + Log.w("XPrivacy", ex.toString()); + } + } + + // AOSP mode override + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !selinux) + try { + Class libcore = Class.forName("libcore.io.Libcore"); + Field fOs = libcore.getDeclaredField("os"); + fOs.setAccessible(true); + Object os = fOs.get(null); + Method setenv = os.getClass().getMethod("setenv", String.class, String.class, boolean.class); + setenv.setAccessible(true); + boolean aosp = new File("/data/system/xprivacy/aosp").exists(); + setenv.invoke(os, "XPrivacy.AOSP", Boolean.toString(aosp), false); + Util.log(null, Log.WARN, "AOSP mode forced=" + aosp); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + /* + * ActivityManagerService is the beginning of the main "android" + * process. This is where the core java system is started, where the + * system context is created and so on. In pre-lollipop we can access + * this class directly, but in lollipop we have to visit ActivityThread + * first, since this class is now responsible for creating a class + * loader that can be used to access ActivityManagerService. It is no + * longer possible to do so via the normal boot class loader. Doing it + * like this will create a consistency between older and newer Android + * versions. + * + * Note that there is no need to handle arguments in this case. And we + * don't need them so in case they change over time, we will simply use + * the hookAll feature. + */ + + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Class at = Class.forName("android.app.ActivityThread"); + XposedBridge.hookAllMethods(at, "systemMain", new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + try { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class am = Class.forName("com.android.server.am.ActivityManagerService", false, loader); + XposedBridge.hookAllConstructors(am, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + try { + PrivacyService.register(mListHookError, loader, mSecret, param.thisObject); + hookSystem(loader); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + + } else { + Class cSystemServer = Class.forName("com.android.server.SystemServer"); + Method mMain = cSystemServer.getDeclaredMethod("main", String[].class); + XposedBridge.hookMethod(mMain, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + try { + PrivacyService.register(mListHookError, null, mSecret, null); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }); + } + + hookZygote(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + hookSystem(null); + + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { + // Check for LBE security master + if (Util.hasLBE()) + return; + + hookPackage(lpparam.packageName, lpparam.classLoader); + } + + private void hookZygote() throws Throwable { + Log.w("XPrivacy", "Hooking Zygote"); + + /* + * Add nixed User Space / System Server hooks + */ + + // Account manager + hookAll(XAccountManager.getInstances(null, false), null, mSecret, false); + + // Activity manager + hookAll(XActivityManager.getInstances(null, false), null, mSecret, false); + + // App widget manager + hookAll(XAppWidgetManager.getInstances(false), null, mSecret, false); + + // Bluetooth adapater + hookAll(XBluetoothAdapter.getInstances(false), null, mSecret, false); + + // Clipboard manager + hookAll(XClipboardManager.getInstances(null, false), null, mSecret, false); + + // Content resolver + hookAll(XContentResolver.getInstances(false), null, mSecret, false); + + // Package manager service + hookAll(XPackageManager.getInstances(null, false), null, mSecret, false); + + // SMS manager + hookAll(XSmsManager.getInstances(false), null, mSecret, false); + + // Telephone service + hookAll(XTelephonyManager.getInstances(null, false), null, mSecret, false); + + // Usage statistics manager + hookAll(XUsageStatsManager.getInstances(false), null, mSecret, false); + + /* + * Add pure user space hooks + */ + + // Intent receive + hookAll(XActivityThread.getInstances(), null, mSecret, false); + + // Runtime + hookAll(XRuntime.getInstances(), null, mSecret, false); + + // Application + hookAll(XApplication.getInstances(), null, mSecret, false); + + // Audio record + hookAll(XAudioRecord.getInstances(), null, mSecret, false); + + // Binder device + hookAll(XBinder.getInstances(), null, mSecret, false); + + // Bluetooth device + hookAll(XBluetoothDevice.getInstances(), null, mSecret, false); + + // Camera + hookAll(XCamera.getInstances(), null, mSecret, false); + + // Camera2 device + hookAll(XCameraDevice2.getInstances(), null, mSecret, false); + + // Connectivity manager + hookAll(XConnectivityManager.getInstances(null, false), null, mSecret, false); + + // Context wrapper + hookAll(XContextImpl.getInstances(), null, mSecret, false); + + // Environment + hookAll(XEnvironment.getInstances(), null, mSecret, false); + + // Inet address + hookAll(XInetAddress.getInstances(), null, mSecret, false); + + // Input device + hookAll(XInputDevice.getInstances(), null, mSecret, false); + + // IO bridge + hookAll(XIoBridge.getInstances(), null, mSecret, false); + + // IP prefix + hookAll(XIpPrefix.getInstances(), null, mSecret, false); + + // Link properties + hookAll(XLinkProperties.getInstances(), null, mSecret, false); + + // Location manager + hookAll(XLocationManager.getInstances(null, false), null, mSecret, false); + + // Media recorder + hookAll(XMediaRecorder.getInstances(), null, mSecret, false); + + // Network info + hookAll(XNetworkInfo.getInstances(), null, mSecret, false); + + // Network interface + hookAll(XNetworkInterface.getInstances(), null, mSecret, false); + + // NFC adapter + hookAll(XNfcAdapter.getInstances(), null, mSecret, false); + + // Process + hookAll(XProcess.getInstances(), null, mSecret, false); + + // Process builder + hookAll(XProcessBuilder.getInstances(), null, mSecret, false); + + // Resources + hookAll(XResources.getInstances(), null, mSecret, false); + + // Sensor manager + hookAll(XSensorManager.getInstances(null, false), null, mSecret, false); + + // Settings secure + hookAll(XSettingsSecure.getInstances(), null, mSecret, false); + + // SIP manager + hookAll(XSipManager.getInstances(), null, mSecret, false); + + // System properties + hookAll(XSystemProperties.getInstances(), null, mSecret, false); + + // USB device + hookAll(XUsbDevice.getInstances(), null, mSecret, false); + + // Web view + hookAll(XWebView.getInstances(), null, mSecret, false); + + // Window service + hookAll(XWindowManager.getInstances(null, false), null, mSecret, false); + + // Wi-Fi service + hookAll(XWifiManager.getInstances(null, false), null, mSecret, false); + + // Intent send + hookAll(XActivity.getInstances(), null, mSecret, false); + } + + private void hookSystem(ClassLoader classLoader) throws Throwable { + Log.w("XPrivacy", "Hooking system"); + + /* + * Add nixed User Space / System Server hooks + */ + + // Account manager + hookAll(XAccountManager.getInstances(null, true), classLoader, mSecret, false); + + // Activity manager + hookAll(XActivityManager.getInstances(null, true), classLoader, mSecret, false); + + // App widget manager + hookAll(XAppWidgetManager.getInstances(true), classLoader, mSecret, false); + + // Bluetooth adapater + hookAll(XBluetoothAdapter.getInstances(true), classLoader, mSecret, false); + + // Clipboard manager + hookAll(XClipboardManager.getInstances(null, true), classLoader, mSecret, false); + + // Content resolver + hookAll(XContentResolver.getInstances(true), classLoader, mSecret, false); + + // Location manager service + hookAll(XLocationManager.getInstances(null, true), classLoader, mSecret, false); + + // Package manager service + hookAll(XPackageManager.getInstances(null, true), classLoader, mSecret, false); + + // SMS manager + hookAll(XSmsManager.getInstances(true), classLoader, mSecret, false); + + // Telephone service + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + hookAll(XTelephonyManager.getInstances(null, true), classLoader, mSecret, false); + hookAll(XTelephonyManager.getRegistryInstances(), classLoader, mSecret, false); + + // Usage statistics manager + hookAll(XUsageStatsManager.getInstances(true), classLoader, mSecret, false); + + // Wi-Fi service + hookAll(XWifiManager.getInstances(null, true), classLoader, mSecret, false); + + /* + * Add pure system server hooks + */ + + // Activity manager service + hookAll(XActivityManagerService.getInstances(), classLoader, mSecret, false); + + // Intent firewall + hookAll(XIntentFirewall.getInstances(), classLoader, mSecret, false); + } + + private void hookPackage(String packageName, ClassLoader classLoader) { + Log.w("XPrivacy", "Hooking package=" + packageName); + + // Skip hooking self + String self = XPrivacy.class.getPackage().getName(); + if (packageName.equals(self)) { + hookAll(XUtilHook.getInstances(), classLoader, mSecret, false); + return; + } + + // Build SERIAL + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || Process.myUid() != Process.SYSTEM_UID) + if (PrivacyManager.getRestrictionExtra(null, Process.myUid(), PrivacyManager.cIdentification, "SERIAL", + null, Build.SERIAL, mSecret)) + try { + Field serial = Build.class.getField("SERIAL"); + serial.setAccessible(true); + serial.set(null, PrivacyManager.getDefacedProp(Process.myUid(), "SERIAL")); + } catch (Throwable ex) { + Util.bug(null, ex); + } + + // Activity recognition + try { + Class.forName("com.google.android.gms.location.ActivityRecognitionClient", false, classLoader); + hookAll(XActivityRecognitionClient.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Advertising Id + try { + Class.forName("com.google.android.gms.ads.identifier.AdvertisingIdClient$Info", false, classLoader); + hookAll(XAdvertisingIdClientInfo.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Cast device + try { + Class.forName("com.google.android.gms.cast.CastDevice", false, classLoader); + hookAll(XCastDevice.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Google auth + try { + Class.forName("com.google.android.gms.auth.GoogleAuthUtil", false, classLoader); + hookAll(XGoogleAuthUtil.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // GoogleApiClient.Builder + try { + Class.forName("com.google.android.gms.common.api.GoogleApiClient$Builder", false, classLoader); + hookAll(XGoogleApiClient.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Google Map V1 + try { + Class.forName("com.google.android.maps.GeoPoint", false, classLoader); + hookAll(XGoogleMapV1.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Google Map V2 + try { + Class.forName("com.google.android.gms.maps.GoogleMap", false, classLoader); + hookAll(XGoogleMapV2.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Location client + try { + Class.forName("com.google.android.gms.location.LocationClient", false, classLoader); + hookAll(XLocationClient.getInstances(), classLoader, mSecret, false); + } catch (Throwable ignored) { + } + + // Phone interface manager + if ("com.android.phone".equals(packageName)) { + hookAll(XTelephonyManager.getPhoneInstances(), classLoader, mSecret, false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + hookAll(XTelephonyManager.getInstances(null, true), classLoader, mSecret, false); + } + + // Providers + hookAll(XContentResolver.getPackageInstances(packageName, classLoader), classLoader, mSecret, false); + } + + public static void handleGetSystemService(String name, String className, String secret) { + if (PrivacyManager.getTransient(className, null) == null) { + PrivacyManager.setTransient(className, Boolean.toString(true)); + + if (name.equals(Context.ACCOUNT_SERVICE)) + hookAll(XAccountManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.ACTIVITY_SERVICE)) + hookAll(XActivityManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.CLIPBOARD_SERVICE)) + hookAll(XClipboardManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.CONNECTIVITY_SERVICE)) + hookAll(XConnectivityManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.LOCATION_SERVICE)) + hookAll(XLocationManager.getInstances(className, false), null, secret, true); + else if (name.equals("PackageManager")) + hookAll(XPackageManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.SENSOR_SERVICE)) + hookAll(XSensorManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.TELEPHONY_SERVICE)) + hookAll(XTelephonyManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.WINDOW_SERVICE)) + hookAll(XWindowManager.getInstances(className, false), null, secret, true); + else if (name.equals(Context.WIFI_SERVICE)) + hookAll(XWifiManager.getInstances(className, false), null, secret, true); + } + } + + public static void hookAll(List listHook, ClassLoader classLoader, String secret, boolean dynamic) { + for (XHook hook : listHook) + if (hook.getRestrictionName() == null) + hook(hook, classLoader, secret); + else { + CRestriction crestriction = new CRestriction(0, hook.getRestrictionName(), null, null); + CRestriction mrestriction = new CRestriction(0, hook.getRestrictionName(), hook.getMethodName(), null); + if (mListDisabled.contains(crestriction) || mListDisabled.contains(mrestriction)) + Util.log(hook, Log.WARN, "Skipping disabled hook " + hook); + else + hook(hook, classLoader, secret); + } + } + + private static void hook(final XHook hook, ClassLoader classLoader, String secret) { + // Get meta data + Hook md = PrivacyManager.getHook(hook.getRestrictionName(), hook.getSpecifier()); + if (md == null) { + String message = "Not found hook=" + hook; + mListHookError.add(message); + Util.log(hook, Log.ERROR, message); + } else if (!md.isAvailable()) + return; + + // Provide secret + if (secret == null) + Util.log(hook, Log.ERROR, "Secret missing hook=" + hook); + hook.setSecret(secret); + + try { + // Find class + Class hookClass = null; + try { + hookClass = findClass(hook.getClassName(), classLoader); + } catch (Throwable ex) { + String message = "Class not found hook=" + hook; + int level = (md != null && md.isOptional() ? Log.WARN : Log.ERROR); + if ("isXposedEnabled".equals(hook.getMethodName())) + level = Log.WARN; + if (level == Log.ERROR) + mListHookError.add(message); + Util.log(hook, level, message); + return; + } + + // Get members + List listMember = new ArrayList(); + List[]> listParameters = new ArrayList[]>(); + Class clazz = hookClass; + while (clazz != null && !"android.content.ContentProvider".equals(clazz.getName())) + try { + if (hook.getMethodName() == null) { + for (Constructor constructor : clazz.getDeclaredConstructors()) + if (!Modifier.isAbstract(constructor.getModifiers()) + && Modifier.isPublic(constructor.getModifiers()) ? hook.isVisible() : !hook + .isVisible()) + listMember.add(constructor); + break; + } else { + for (Method method : clazz.getDeclaredMethods()) + if (method.getName().equals(hook.getMethodName()) + && !Modifier.isAbstract(method.getModifiers()) + && (Modifier.isPublic(method.getModifiers()) ? hook.isVisible() : !hook.isVisible())) { + + // Check for same function in sub class + boolean different = true; + for (Class[] parameters : listParameters) { + boolean same = (parameters.length == method.getParameterTypes().length); + for (int p = 0; same && p < parameters.length; p++) + if (!parameters[p].equals(method.getParameterTypes()[p])) { + same = false; + break; + } + if (same) { + different = false; + break; + } + } + + if (different) { + listMember.add(method); + listParameters.add(method.getParameterTypes()); + } + } + } + clazz = clazz.getSuperclass(); + } catch (Throwable ex) { + if (ex.getClass().equals(ClassNotFoundException.class) + || ex.getClass().equals(NoClassDefFoundError.class)) + break; + else + throw ex; + } + + // Hook members + for (Member member : listMember) + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + if ((member.getModifiers() & Modifier.NATIVE) != 0) + Util.log(hook, Log.WARN, "Native method=" + member); + XposedBridge.hookMethod(member, new XMethodHook(hook)); + } catch (NoSuchFieldError ex) { + Util.log(hook, Log.WARN, ex.toString()); + } catch (Throwable ex) { + mListHookError.add(ex.toString()); + Util.bug(hook, ex); + } + + // Check if members found + if (listMember.isEmpty() && !hook.getClassName().startsWith("com.google.android.gms")) { + String message = "Method not found hook=" + hook; + int level = (md != null && md.isOptional() ? Log.WARN : Log.ERROR); + if ("isXposedEnabled".equals(hook.getMethodName())) + level = Log.WARN; + if (level == Log.ERROR) + mListHookError.add(message); + Util.log(hook, level, message); + } + } catch (Throwable ex) { + mListHookError.add(ex.toString()); + Util.bug(hook, ex); + } + } + + // Helper classes + + private static class XMethodHook extends XC_MethodHook { + private XHook mHook; + + public XMethodHook(XHook hook) { + mHook = hook; + } + + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + try { + // Do not restrict Zygote + if (Process.myUid() <= 0) + return; + + // Pre processing + XParam xparam = XParam.fromXposed(param); + + long start = System.currentTimeMillis(); + + // Execute hook + mHook.before(xparam); + + long ms = System.currentTimeMillis() - start; + if (ms > PrivacyManager.cWarnHookDelayMs) + Util.log(mHook, Log.WARN, String.format("%s %d ms", param.method.getName(), ms)); + + // Post processing + if (xparam.hasResult()) + param.setResult(xparam.getResult()); + if (xparam.hasThrowable()) + param.setThrowable(xparam.getThrowable()); + param.setObjectExtra("xextra", xparam.getExtras()); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + if (!param.hasThrowable()) + try { + // Do not restrict Zygote + if (Process.myUid() <= 0) + return; + + // Pre processing + XParam xparam = XParam.fromXposed(param); + xparam.setExtras(param.getObjectExtra("xextra")); + + long start = System.currentTimeMillis(); + + // Execute hook + mHook.after(xparam); + + long ms = System.currentTimeMillis() - start; + if (ms > PrivacyManager.cWarnHookDelayMs) + Util.log(mHook, Log.WARN, String.format("%s %d ms", param.method.getName(), ms)); + + // Post processing + if (xparam.hasResult()) + param.setResult(xparam.getResult()); + if (xparam.hasThrowable()) + param.setThrowable(xparam.getThrowable()); + } catch (Throwable ex) { + Util.bug(null, ex); + } + } + }; +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XProcess.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XProcess.java new file mode 100644 index 0000000..f8264d9 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XProcess.java @@ -0,0 +1,167 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +public class XProcess extends XHook { + private Methods mMethod; + private String mRestrictionName; + private String mAction; + + private XProcess(Methods method, String restrictionName, String action) { + super(restrictionName, method.name(), action); + mMethod = method; + mRestrictionName = restrictionName; + mAction = action; + } + + public String getClassName() { + return "android.os.Process"; + } + + public boolean isVisible() { + return false; + } + + private enum Methods { + startViaZygote + }; + + // @formatter:off + + // private static ProcessStartResult startViaZygote( + // final String processClass, final String niceName, + // final int uid, final int gid, final int[] gids, ... + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/os/Process.java + + // @formatter:on + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cInternet, "inet")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cInternet, "inet_admin")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cInternet, "inet_bw")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cInternet, "inet_vpn")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cInternet, "inet_mesh")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cStorage, "media")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cStorage, "sdcard")); + listHook.add(new XProcess(Methods.startViaZygote, PrivacyManager.cStorage, "mtp")); + return listHook; + } + + final static int sdcard_r = 1028; // 4.1+ + final static int sdcard_rw = 1015; // 4.0+ + final static int media_rw = 1023; // 4.0+ + final static int mtp = 1024; + + final static int sdcard_pics = 1033; // 4.4+ photos + final static int sdcard_av = 1034; // 4.4+ audio/video + final static int sdcard_all = 1035; // 4.4+ all users + + final static int inet = 3003; // 4.0+ + final static int inet_raw = 3004; // 4.0+ + final static int inet_admin = 3005; + final static int inet_bw_stats = 3006; + final static int inet_bw_acct = 3007; + final static int inet_vpn = 1016; + final static int inet_mesh = 1030; + + // frameworks/base/data/etc/platform.xml + // https://android.googlesource.com/platform/system/core/+/master/include/private/android_filesystem_config.h + + // http://www.doubleencore.com/2014/03/android-external-storage/ + // http://www.chainfire.eu/articles/113/Is_Google_blocking_apps_writing_to_SD_cards_/ + // https://android.googlesource.com/platform/system/core/+/dfe0cba + // https://android.googlesource.com/platform/system/core/+/master/sdcard/sdcard.c + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case startViaZygote: + if (param.args.length >= 5 && param.args[2] instanceof Integer && param.args[4] instanceof int[]) { + // Get IDs + int uid = (Integer) param.args[2]; + int[] gids = (int[]) param.args[4]; + + // Build list of modified gids + List listGids = new ArrayList(); + for (int i = 0; i < gids.length; i++) { + if (gids[i] == media_rw) + if (mRestrictionName.equals(PrivacyManager.cStorage) && mAction.equals("media") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking media uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == sdcard_r || gids[i] == sdcard_rw || gids[i] == sdcard_all + || gids[i] == sdcard_pics || gids[i] == sdcard_av) + if (mRestrictionName.equals(PrivacyManager.cStorage) && mAction.equals("sdcard") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking sdcard uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == mtp) + if (mRestrictionName.equals(PrivacyManager.cStorage) && mAction.equals("mtp") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking mtp uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == inet || gids[i] == inet_raw) + if (mRestrictionName.equals(PrivacyManager.cInternet) && mAction.equals("inet") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking inet uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == inet_admin) + if (mRestrictionName.equals(PrivacyManager.cInternet) && mAction.equals("inet_admin") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking inet_admin uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == inet_bw_stats || gids[i] == inet_bw_acct) + if (mRestrictionName.equals(PrivacyManager.cInternet) && mAction.equals("inet_bw") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking inet_bw uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == inet_vpn) + if (mRestrictionName.equals(PrivacyManager.cInternet) && mAction.equals("inet_vpn") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking inet_vpn uid=" + uid); + else + listGids.add(gids[i]); + + else if (gids[i] == inet_mesh) + if (mRestrictionName.equals(PrivacyManager.cInternet) && mAction.equals("inet_mesh") + && getRestricted(uid, mAction)) + Util.log(this, Log.INFO, "Revoking inet_mesh uid=" + uid); + else + listGids.add(gids[i]); + + else + listGids.add(gids[i]); + } + + // Proces list of modified gids + int[] mGids = new int[listGids.size()]; + for (int i = 0; i < listGids.size(); i++) + mGids[i] = listGids.get(i); + + param.args[4] = (mGids.length == 0 ? null : mGids); + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XProcessBuilder.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XProcessBuilder.java new file mode 100644 index 0000000..4cdc0e6 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XProcessBuilder.java @@ -0,0 +1,57 @@ +package biz.bokhorst.xprivacy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import android.text.TextUtils; +import android.util.Log; + +public class XProcessBuilder extends XHook { + + private String mCommand; + + private XProcessBuilder(String methodName, String restrictionName, String command) { + super(restrictionName, methodName, command); + mCommand = command; + } + + public String getClassName() { + return "java.lang.ProcessBuilder"; + } + + // public Process start() + // libcore/luni/src/main/java/java/lang/ProcessBuilder.java + // http://developer.android.com/reference/java/lang/ProcessBuilder.html + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XProcessBuilder("start", PrivacyManager.cShell, "sh")); + listHook.add(new XProcessBuilder("start", PrivacyManager.cShell, "su")); + listHook.add(new XProcessBuilder("start", PrivacyManager.cShell, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + String methodName = param.method.getName(); + if (methodName.equals("start")) { + // Get commands + ProcessBuilder builder = (ProcessBuilder) param.thisObject; + List listProg = (builder == null ? null : builder.command()); + + // Check commands + if (listProg != null) { + String command = TextUtils.join(" ", listProg); + if (XRuntime.matches(command, mCommand) && isRestrictedExtra(param, command)) + param.setThrowable(new IOException("XPrivacy")); + } + } else + Util.log(this, Log.WARN, "Unknown method=" + methodName); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XResources.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XResources.java new file mode 100644 index 0000000..90975d9 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XResources.java @@ -0,0 +1,75 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.content.res.Configuration; +import android.os.Binder; +import android.util.Log; + +public class XResources extends XHook { + private Methods mMethod; + + private XResources(Methods method) { + super(null, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return "android.content.res.Resources"; + } + + // public void updateConfiguration(Configuration config, ...) + // frameworks/base/core/java/android/content/res/Resources.java + // http://developer.android.com/reference/android/content/res/Resources.html + // http://developer.android.com/reference/android/content/res/Configuration.html + + private enum Methods { + updateConfiguration + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XResources(Methods.updateConfiguration)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.updateConfiguration) { + if (param.args.length > 0 && param.args[0] != null && param.args[0] instanceof Configuration) { + boolean restricted = false; + int uid = Binder.getCallingUid(); + Configuration config = new Configuration((Configuration) param.args[0]); + + if (getRestricted(uid, PrivacyManager.cPhone, "Configuration.MCC")) { + restricted = true; + try { + config.mcc = Integer.parseInt((String) PrivacyManager.getDefacedProp(uid, "MCC")); + } catch (Throwable ex) { + config.mcc = 1; + } + } + + if (getRestricted(uid, PrivacyManager.cPhone, "Configuration.MNC")) { + restricted = true; + try { + config.mnc = Integer.parseInt((String) PrivacyManager.getDefacedProp(uid, "MNC")); + } catch (Throwable ex) { + config.mnc = 1; + } + } + + if (restricted) + param.args[0] = config; + } + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XRuntime.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XRuntime.java new file mode 100644 index 0000000..dd76fee --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XRuntime.java @@ -0,0 +1,111 @@ +package biz.bokhorst.xprivacy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import android.os.Build; +import android.os.Process; +import android.text.TextUtils; + +public class XRuntime extends XHook { + private Methods mMethod; + private String mCommand; + + private XRuntime(Methods method, String restrictionName, String command) { + super(restrictionName, method.name(), command); + mMethod = method; + mCommand = command; + } + + public String getClassName() { + return "java.lang.Runtime"; + } + + @Override + public boolean isVisible() { + return !(mMethod == Methods.load || mMethod == Methods.loadLibrary); + } + + // public Process exec(String[] progArray) + // public Process exec(String[] progArray, String[] envp) + // public Process exec(String[] progArray, String[] envp, File directory) + // public Process exec(String prog) + // public Process exec(String prog, String[] envp) + // public Process exec(String prog, String[] envp, File directory) + // public void load(String pathName) + // public void loadLibrary(String libName) + // libcore/luni/src/main/java/java/lang/Runtime.java + // http://developer.android.com/reference/java/lang/Runtime.html + + private enum Methods { + exec, load, loadLibrary + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XRuntime(Methods.exec, PrivacyManager.cShell, "sh")); + listHook.add(new XRuntime(Methods.exec, PrivacyManager.cShell, "su")); + listHook.add(new XRuntime(Methods.exec, PrivacyManager.cShell, null)); + listHook.add(new XRuntime(Methods.load, PrivacyManager.cShell, null)); + listHook.add(new XRuntime(Methods.loadLibrary, PrivacyManager.cShell, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case exec: + // Get programs + String[] progs = null; + if (param.args.length > 0 && param.args[0] != null) + if (String.class.isAssignableFrom(param.args[0].getClass())) + progs = new String[] { (String) param.args[0] }; + else + progs = (String[]) param.args[0]; + + // Check programs + if (progs != null) { + String command = TextUtils.join(" ", progs); + if (matches(command, mCommand) && isRestrictedExtra(param, command)) + param.setThrowable(new IOException("XPrivacy")); + } + break; + + case load: + case loadLibrary: + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || Process.myUid() != Process.SYSTEM_UID) + if (param.args.length > 0) { + String libName = (String) param.args[0]; + if (isRestrictedExtra(param, libName)) + param.setThrowable(new UnsatisfiedLinkError("XPrivacy")); + } + + break; + } + } + + @Override + protected void after(final XParam param) throws Throwable { + // Do nothing + } + + public static boolean matches(String command, String mCommand) { + if (mCommand == null) + return !isShell(command) && !isSU(command); + else if (mCommand.equals("sh")) + return isShell(command); + else if (mCommand.equals("su")) + return isSU(command); + else + return false; + } + + private static boolean isShell(String command) { + return command.startsWith("sh") || command.matches("/.*/.*/sh.*") || command.contains("sh "); + } + + private static boolean isSU(String command) { + return command.startsWith("su") || command.matches("/.*/.*/su.*") || command.contains("su "); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSensorManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSensorManager.java new file mode 100644 index 0000000..be5d308 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSensorManager.java @@ -0,0 +1,182 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.util.Log; + +public class XSensorManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.hardware.SensorManager"; + + private static final int cMaxRateUs = (int) (0.01 * 1000 * 1000); // 100 Hz + + private XSensorManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public Sensor getDefaultSensor(int type) + // public List getSensorList(int type) + // boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs, int maxBatchReportLatencyUs) + // boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs, Handler handler) + // boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs, int maxBatchReportLatencyUs, Handler handler) + // boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs) + // frameworks/base/core/java/android/hardware/SensorManager.java + // http://developer.android.com/reference/android/hardware/SensorManager.html + // http://developer.android.com/reference/android/hardware/Sensor.html + + // @formatter:on + + private enum Methods { + getDefaultSensor, getSensorList, registerListener + }; + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + listHook.add(new XSensorManager(Methods.getDefaultSensor, PrivacyManager.cSensors, className)); + listHook.add(new XSensorManager(Methods.getSensorList, PrivacyManager.cSensors, className)); + listHook.add(new XSensorManager(Methods.registerListener, PrivacyManager.cSensors, className)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getDefaultSensor: + if (isRestricted(param)) + param.setResult(null); + else if (param.args.length > 0 && param.args[0] instanceof Integer) + if (isRestricted(param, (Integer) param.args[0])) + param.setResult(null); + break; + + case getSensorList: + if (isRestricted(param)) + param.setResult(new ArrayList()); + else if (param.args.length > 0 && param.args[0] instanceof Integer) + if (isRestricted(param, (Integer) param.args[0])) + param.setResult(new ArrayList()); + break; + + case registerListener: + if (param.args.length > 2 && param.args[1] instanceof Sensor && param.args[2] instanceof Integer) { + int type = ((Sensor) param.args[1]).getType(); + if (type == Sensor.TYPE_GYROSCOPE || type == Sensor.TYPE_GYROSCOPE_UNCALIBRATED) { + int rateUs = (Integer) param.args[2]; + + // http://developer.android.com/guide/topics/sensors/sensors_overview.html + if (rateUs == SensorManager.SENSOR_DELAY_NORMAL) + return; // 200,000 us + else if (rateUs == SensorManager.SENSOR_DELAY_UI) + return; // 60,000 us + else if (rateUs == SensorManager.SENSOR_DELAY_GAME) + return; // 20,000 us + else if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) + ; // 0 us + + if (rateUs < cMaxRateUs) // 10,000 us + if (isRestricted(param)) + param.args[2] = cMaxRateUs; + } + } + break; + } + } + + @Override + @SuppressWarnings("unchecked") + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getDefaultSensor: + case registerListener: + // Do nothing + break; + + case getSensorList: + if (param.getResult() != null && param.args.length > 0 && param.args[0] instanceof Integer) + if ((Integer) param.args[0] == Sensor.TYPE_ALL) { + List listSensor = new ArrayList(); + for (Sensor sensor : (List) param.getResult()) + if (!isRestricted(param, sensor.getType())) + listSensor.add(sensor); + param.setResult(listSensor); + } + break; + } + } + + @SuppressWarnings("deprecation") + private boolean isRestricted(XParam param, int type) throws Throwable { + if (type == Sensor.TYPE_ALL) + return false; + else if (type == Sensor.TYPE_ACCELEROMETER || type == Sensor.TYPE_LINEAR_ACCELERATION) { + if (isRestricted(param, "acceleration")) + return true; + } else if (type == Sensor.TYPE_GRAVITY) { + if (isRestricted(param, "gravity")) + return true; + } else if (type == Sensor.TYPE_RELATIVE_HUMIDITY) { + if (isRestricted(param, "humidity")) + return true; + } else if (type == Sensor.TYPE_LIGHT) { + if (isRestricted(param, "light")) + return true; + } else if (type == Sensor.TYPE_MAGNETIC_FIELD || type == Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED) { + if (isRestricted(param, "magnetic")) + return true; + } else if (type == Sensor.TYPE_SIGNIFICANT_MOTION) { + if (isRestricted(param, "motion")) + return true; + } else if (type == Sensor.TYPE_ORIENTATION || type == Sensor.TYPE_GYROSCOPE + || type == Sensor.TYPE_GYROSCOPE_UNCALIBRATED) { + if (isRestricted(param, "orientation")) + return true; + } else if (type == Sensor.TYPE_PRESSURE) { + if (isRestricted(param, "pressure")) + return true; + } else if (type == Sensor.TYPE_PROXIMITY) { + if (isRestricted(param, "proximity")) + return true; + } else if (type == Sensor.TYPE_GAME_ROTATION_VECTOR || type == Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR + || type == Sensor.TYPE_ROTATION_VECTOR) { + if (isRestricted(param, "rotation")) + return true; + } else if (type == Sensor.TYPE_TEMPERATURE || type == Sensor.TYPE_AMBIENT_TEMPERATURE) { + if (isRestricted(param, "temperature")) + return true; + } else if (type == Sensor.TYPE_STEP_COUNTER || type == Sensor.TYPE_STEP_DETECTOR) { + if (isRestricted(param, "step")) + return true; + } else if (type == Sensor.TYPE_HEART_RATE) { + if (isRestricted(param, "heartrate")) + return true; + } else if (type == 22) { + // 22 = TYPE_TILT_DETECTOR + // Do nothing + } else if (type == 23 || type == 24 || type == 25) { + // 23 = TYPE_WAKE_GESTURE + // 24 = TYPE_GLANCE_GESTURE + // 25 = TYPE_PICK_UP_GESTURE + // 23/24 This sensor is expected to only be used by the system ui + // 25 Expected to be used internally for always on display + } else + Util.log(this, Log.WARN, "Unknown sensor type=" + type); + return false; + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSettingsSecure.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSettingsSecure.java new file mode 100644 index 0000000..a75350a --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSettingsSecure.java @@ -0,0 +1,60 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.provider.Settings; +import android.util.Log; + +public class XSettingsSecure extends XHook { + private Methods mMethod; + + private XSettingsSecure(Methods method, String restrictionName) { + super(restrictionName, method.name(), null); + mMethod = method; + } + + public String getClassName() { + return "android.provider.Settings.Secure"; + } + + // @formatter:off + + // public synchronized static String getString(ContentResolver resolver, String name) + // frameworks/base/core/java/android/provider/Settings.java + // frameworks/base/core/java/android/content/ContentResolver.java + // http://developer.android.com/reference/android/provider/Settings.Secure.html + + // @formatter:on + + private enum Methods { + getString + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XSettingsSecure(Methods.getString, PrivacyManager.cIdentification)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + if (mMethod == Methods.getString) { + String name = (param.args.length > 1 ? (String) param.args[1] : null); + if (Settings.Secure.ANDROID_ID.equals(name)) { + String id = (String) param.getResult(); + if (id != null) + if (isRestrictedValue(param, id)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "ANDROID_ID")); + } + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSipManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSipManager.java new file mode 100644 index 0000000..7b1ad76 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSipManager.java @@ -0,0 +1,61 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +public class XSipManager extends XHook { + private Methods mMethod; + + private XSipManager(Methods method, String restrictionName) { + super(restrictionName, method.name(), "SIP." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.net.sip.SipManager"; + } + + // @formatter:off + + // static boolean isApiSupported(Context context) + // static boolean isSipWifiOnly(Context context) + // static boolean isVoipSupported(Context context) + // public static SipManager newInstance (Context context) + // http://developer.android.com/reference/android/net/sip/SipManager.html + + // @formatter:on + + private enum Methods { + isApiSupported, isSipWifiOnly, isVoipSupported, newInstance + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XSipManager(Methods.isApiSupported, PrivacyManager.cCalling)); + listHook.add(new XSipManager(Methods.isSipWifiOnly, PrivacyManager.cCalling)); + listHook.add(new XSipManager(Methods.isVoipSupported, PrivacyManager.cCalling)); + listHook.add(new XSipManager(Methods.newInstance, PrivacyManager.cCalling)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.isApiSupported || mMethod == Methods.isSipWifiOnly || mMethod == Methods.isVoipSupported) { + if (isRestricted(param)) + param.setResult(false); + + } else if (mMethod == Methods.newInstance) { + if (isRestricted(param)) + param.setResult(null); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSmsManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSmsManager.java new file mode 100644 index 0000000..241beab --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSmsManager.java @@ -0,0 +1,136 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Bundle; +import android.telephony.SmsMessage; + +public class XSmsManager extends XHook { + private Methods mMethod; + + private XSmsManager(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + } + + public String getClassName() { + if (mMethod.name().startsWith("Srv_")) + return "com.android.internal.telephony.IccSmsInterfaceManager"; + else + return "android.telephony.SmsManager"; + } + + // @formatter:off + + // public static ArrayList getAllMessagesFromIcc() + // public Bundle getCarrierConfigValues() + // public void sendDataMessage(String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) + // public void sendMultipartTextMessage(String destinationAddress, String scAddress, ArrayList parts, ArrayList sentIntents, ArrayList deliveryIntents) + // public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) + // public void sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) + // frameworks/base/telephony/java/android/telephony/SmsManager.java + // http://developer.android.com/reference/android/telephony/SmsManager.html + + // public List getAllMessagesFromIccEf(String callingPackage) + // public void sendData(java.lang.String callingPkg, java.lang.String destAddr, java.lang.String scAddr, int destPort, byte[] data, android.app.PendingIntent sentIntent, android.app.PendingIntent deliveryIntent) + // public void sendMultipartText(java.lang.String callingPkg, java.lang.String destinationAddress, java.lang.String scAddress, java.util.List parts, java.util.List sentIntents, java.util.List deliveryIntents) + // public void sendText(java.lang.String callingPkg, java.lang.String destAddr, java.lang.String scAddr, java.lang.String text, android.app.PendingIntent sentIntent, android.app.PendingIntent deliveryIntent) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/internal/telephony/IccSmsInterfaceManager.java/ + + // @formatter:on + + // @formatter:off + private enum Methods { + getAllMessagesFromIcc, getCarrierConfigValues, sendDataMessage, sendMultipartTextMessage, sendMultimediaMessage, sendTextMessage, + Srv_getAllMessagesFromIccEf, Srv_sendData, Srv_sendMultipartText, Srv_sendText + }; + // @formatter:on + + public static List getInstances(boolean server) { + List listHook = new ArrayList(); + if (server) { + listHook.add(new XSmsManager(Methods.Srv_getAllMessagesFromIccEf, PrivacyManager.cMessages)); + listHook.add(new XSmsManager(Methods.Srv_sendData, PrivacyManager.cCalling)); + listHook.add(new XSmsManager(Methods.Srv_sendMultipartText, PrivacyManager.cCalling)); + listHook.add(new XSmsManager(Methods.Srv_sendText, PrivacyManager.cCalling)); + } else { + listHook.add(new XSmsManager(Methods.getAllMessagesFromIcc, PrivacyManager.cMessages)); + listHook.add(new XSmsManager(Methods.getCarrierConfigValues, PrivacyManager.cMessages)); + listHook.add(new XSmsManager(Methods.sendDataMessage, PrivacyManager.cCalling)); + listHook.add(new XSmsManager(Methods.sendMultimediaMessage, PrivacyManager.cCalling)); + listHook.add(new XSmsManager(Methods.sendMultipartTextMessage, PrivacyManager.cCalling)); + listHook.add(new XSmsManager(Methods.sendTextMessage, PrivacyManager.cCalling)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getAllMessagesFromIcc: + case getCarrierConfigValues: + // Do nothing + break; + + case sendDataMessage: + case sendMultipartTextMessage: + case sendTextMessage: + if (param.args.length > 0 && param.args[0] instanceof String) + if (isRestrictedExtra(param, (String) param.args[0])) + param.setResult(null); + break; + + case sendMultimediaMessage: + if (isRestricted(param)) + param.setResult(null); + break; + + case Srv_getAllMessagesFromIccEf: + // Do nothing + break; + + case Srv_sendData: + case Srv_sendText: + case Srv_sendMultipartText: + if (param.args.length > 1 && (param.args[1] == null || param.args[1] instanceof String)) + if (isRestrictedExtra(param, (String) param.args[1])) + param.setResult(null); + break; + } + } + + @Override + @SuppressWarnings("rawtypes") + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getAllMessagesFromIcc: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getCarrierConfigValues: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new Bundle()); + break; + + case sendDataMessage: + case sendMultimediaMessage: + case sendMultipartTextMessage: + case sendTextMessage: + // Do nothing + break; + + case Srv_getAllMessagesFromIccEf: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case Srv_sendData: + case Srv_sendText: + case Srv_sendMultipartText: + // Do nothing + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSystemProperties.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSystemProperties.java new file mode 100644 index 0000000..b7b6fc4 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XSystemProperties.java @@ -0,0 +1,65 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XSystemProperties extends XHook { + private Methods mMethod; + private String mPropertyName; + + private XSystemProperties(Methods method, String restrictionName, String propertyName) { + super(restrictionName, method.name(), propertyName); + mMethod = method; + mPropertyName = propertyName; + } + + public String getClassName() { + return "android.os.SystemProperties"; + } + + // public static String get(String key) + // public static String get(String key, String def) + // public static boolean getBoolean(String key, boolean def) + // public static int getInt(String key, int def) + // public static long getLong(String key, long def) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/os/SystemProperties.java/ + + private enum Methods { + get, getBoolean, getInt, getLong + }; + + public static List getInstances() { + List listHook = new ArrayList(); + String[] props = new String[] { "%imei", "%hostname", "%serialno", "%macaddr", "%cid" }; + for (String prop : props) + for (Methods getter : Methods.values()) + listHook.add(new XSystemProperties(getter, PrivacyManager.cIdentification, prop)); + + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + String key = (param.args.length > 0 ? (String) param.args[0] : null); + if (key != null) + if (mPropertyName.startsWith("%") ? key.contains(mPropertyName.substring(1)) : key.equals(mPropertyName)) + if (mMethod == Methods.get) { + if (param.getResult() != null && isRestrictedExtra(param, mPropertyName, key)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), mPropertyName)); + + } else if (param.args.length > 1) { + if (isRestrictedExtra(param, mPropertyName, key)) + param.setResult(param.args[1]); + + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } +} \ No newline at end of file diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XTelephonyManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XTelephonyManager.java new file mode 100644 index 0000000..8fc6ad7 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XTelephonyManager.java @@ -0,0 +1,672 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.telephony.CellLocation; +import android.telephony.NeighboringCellInfo; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.CellInfo; +import android.telephony.gsm.GsmCellLocation; +import android.util.Log; + +public class XTelephonyManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.telephony.TelephonyManager"; + private static final Map mListener = new WeakHashMap(); + + private enum Srv { + SubInfo, Registry, Phone, SICtl + }; + + private XTelephonyManager(Methods method, String restrictionName, Srv srv) { + super(restrictionName, method.name().replace("Srv_", "").replace("5", ""), method.name()); + mMethod = method; + if (srv == Srv.SubInfo) + mClassName = "com.android.internal.telephony.PhoneSubInfo"; + else if (srv == Srv.Registry) + mClassName = "com.android.server.TelephonyRegistry"; + else if (srv == Srv.Phone) + mClassName = "com.android.phone.PhoneInterfaceManager"; + else if (srv == Srv.SICtl) + mClassName = "com.android.internal.telephony.PhoneSubInfoController"; + else + Util.log(null, Log.ERROR, "Unknown srv=" + srv.name()); + } + + private XTelephonyManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public void disableLocationUpdates() + // public void enableLocationUpdates() + // public List getAllCellInfo() + // public CellLocation getCellLocation() + // public String getDeviceId() + // public String getGroupIdLevel1() + // public String getIsimDomain() + // public String getIsimImpi() + // public String[] getIsimImpu() + // public String getLine1AlphaTag() + // public String getLine1Number() + // public String getMsisdn() + // public List getNeighboringCellInfo() + // public String getNetworkCountryIso() + // public String getNetworkOperator() + // public String getNetworkOperatorName() + // public String getSimCountryIso() + // public String getSimOperator() + // public String getSimOperatorName() + // public static int getPhoneType(int networkMode) + // public String getSimSerialNumber() + // public String getSubscriberId() + // public String getVoiceMailAlphaTag() + // public String getVoiceMailNumber() + // public void listen(PhoneStateListener listener, int events) + // frameworks/base/telephony/java/android/telephony/TelephonyManager.java + // http://developer.android.com/reference/android/telephony/TelephonyManager.html + + // public String getDeviceId() + // public String getSubscriberId() + // public String getGroupIdLevel1() + // public String getIccSerialNumber() + // public String getImei() + // public String getLine1Number() + // public String getLine1AlphaTag() + // public String getMsisdn() + // public String getVoiceMailNumber() + // public String getVoiceMailAlphaTag() + // public String getCompleteVoiceMailNumber() + // public String getIsimImpi() + // public String getIsimDomain() + // public String[] getIsimImpu() + // public String getIsimIst() + // public String[] getIsimPcscf() + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/internal/telephony/PhoneSubInfo.java + + // public void listen(java.lang.String pkg, IPhoneStateListener callback, int events, boolean notifyNow) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/TelephonyRegistry.java + + // public void enableLocationUpdates() + // public void enableLocationUpdatesForSubscriber(long subId) + // public void disableLocationUpdates() + // public void disableLocationUpdatesForSubscriber(long subId) + // public List getAllCellInfo() + // public android.os.Bundle getCellLocation() + // public String getCdmaMdn(long subId) + // public String getCdmaMin(long subId) + // public String getLine1AlphaTagForDisplay(long subId) + // public String getLine1NumberForDisplay(long subId) + // public List getNeighboringCellInfo() + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/internal/telephony/ITelephony.java + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/5.0.0_r1/com/android/phone/PhoneInterfaceManager.java + + // @formatter:on + + // @formatter:off + private enum Methods { + disableLocationUpdates, enableLocationUpdates, + getAllCellInfo, getCellLocation, + getDeviceId, getGroupIdLevel1, + getIsimDomain, getIsimImpi, getIsimImpu, + getLine1AlphaTag, getLine1Number, getMsisdn, + getNeighboringCellInfo, + getNetworkCountryIso, getNetworkOperator, getNetworkOperatorName, + getSimCountryIso, getSimOperator, getSimOperatorName, getSimSerialNumber, + getSubscriberId, + getVoiceMailAlphaTag, getVoiceMailNumber, + listen, + + Srv_getDeviceId, Srv_getGroupIdLevel1, + Srv_getIccSerialNumber, + Srv_getIsimDomain, Srv_getIsimImpi, Srv_getIsimImpu, + Srv_getLine1AlphaTag, Srv_getLine1Number, + Srv_getMsisdn, + Srv_getSubscriberId, + Srv_getCompleteVoiceMailNumber, Srv_getVoiceMailNumber, Srv_getVoiceMailAlphaTag, + Srv_getImei, Srv_getIsimIst, Srv_getIsimPcscf, + + Srv_listen, + + Srv_enableLocationUpdates, Srv_disableLocationUpdates, + Srv_getAllCellInfo, Srv_getCellLocation, Srv_getNeighboringCellInfo, + + Srv_enableLocationUpdatesForSubscriber, Srv_disableLocationUpdatesForSubscriber, + Srv_getCdmaMdn, Srv_getCdmaMin, + Srv_getLine1AlphaTagForDisplay, Srv_getLine1NumberForDisplay, + + // Android 5.x + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.0_r1/com/android/internal/telephony/PhoneSubInfoController.java + Srv_getCompleteVoiceMailNumberForSubscriber5, + Srv_getDeviceId5, + Srv_getDeviceIdForPhone5, + Srv_getDeviceIdForSubscriber5, + Srv_getGroupIdLevel1ForSubscriber5, + Srv_getIccSerialNumberForSubscriber5, + Srv_getImeiForSubscriber5, + Srv_getIsimDomain5, + Srv_getIsimImpi5, + Srv_getIsimImpu5, + Srv_getIsimIst5, + Srv_getIsimPcscf5, + Srv_getLine1AlphaTagForSubscriber5, + Srv_getLine1NumberForSubscriber5, + Srv_getMsisdnForSubscriber5, + Srv_getNaiForSubscriber5, // new + Srv_getSubscriberIdForSubscriber5, + Srv_getVoiceMailAlphaTagForSubscriber5, + Srv_getVoiceMailNumberForSubscriber5 + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + if (server) { + // PhoneSubInfo/Controller + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + listHook.add(new XTelephonyManager(Methods.Srv_getDeviceId, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getGroupIdLevel1, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getIccSerialNumber, PrivacyManager.cPhone, + Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimDomain, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimImpi, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimImpu, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getLine1AlphaTag, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getLine1Number, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getMsisdn, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getSubscriberId, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getCompleteVoiceMailNumber, PrivacyManager.cPhone, + Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getVoiceMailAlphaTag, PrivacyManager.cPhone, + Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getVoiceMailNumber, PrivacyManager.cPhone, + Srv.SubInfo)); + + listHook.add(new XTelephonyManager(Methods.Srv_getImei, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimIst, PrivacyManager.cPhone, Srv.SubInfo)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimPcscf, PrivacyManager.cPhone, Srv.SubInfo)); + } else { + listHook.add(new XTelephonyManager(Methods.Srv_getDeviceIdForPhone5, PrivacyManager.cPhone, + Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getDeviceIdForSubscriber5, PrivacyManager.cPhone, + Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getGroupIdLevel1ForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getIccSerialNumberForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimDomain5, PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimImpi5, PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimImpu5, PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getLine1AlphaTagForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getLine1NumberForSubscriber5, PrivacyManager.cPhone, + Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getMsisdnForSubscriber5, PrivacyManager.cPhone, + Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getSubscriberIdForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getCompleteVoiceMailNumberForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getVoiceMailAlphaTagForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getVoiceMailNumberForSubscriber5, + PrivacyManager.cPhone, Srv.SICtl)); + + listHook.add(new XTelephonyManager(Methods.Srv_getImeiForSubscriber5, PrivacyManager.cPhone, + Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimIst5, PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getIsimPcscf5, PrivacyManager.cPhone, Srv.SICtl)); + listHook.add(new XTelephonyManager(Methods.Srv_getNaiForSubscriber5, PrivacyManager.cPhone, + Srv.SICtl)); + } + } else { + listHook.add(new XTelephonyManager(Methods.disableLocationUpdates, null, className)); + listHook.add(new XTelephonyManager(Methods.enableLocationUpdates, PrivacyManager.cLocation, className)); + listHook.add(new XTelephonyManager(Methods.getAllCellInfo, PrivacyManager.cLocation, className)); + listHook.add(new XTelephonyManager(Methods.getCellLocation, PrivacyManager.cLocation, className)); + + listHook.add(new XTelephonyManager(Methods.getDeviceId, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getGroupIdLevel1, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getIsimDomain, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getIsimImpi, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getIsimImpu, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getLine1AlphaTag, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getLine1Number, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getMsisdn, PrivacyManager.cPhone, className)); + + listHook.add(new XTelephonyManager(Methods.getNeighboringCellInfo, PrivacyManager.cLocation, className)); + + listHook.add(new XTelephonyManager(Methods.getSimSerialNumber, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getSubscriberId, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getVoiceMailAlphaTag, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getVoiceMailNumber, PrivacyManager.cPhone, className)); + + listHook.add(new XTelephonyManager(Methods.listen, PrivacyManager.cLocation, className)); + listHook.add(new XTelephonyManager(Methods.listen, PrivacyManager.cPhone, className)); + + // No permissions required + listHook.add(new XTelephonyManager(Methods.getNetworkCountryIso, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getNetworkOperator, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getNetworkOperatorName, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getSimCountryIso, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getSimOperator, PrivacyManager.cPhone, className)); + listHook.add(new XTelephonyManager(Methods.getSimOperatorName, PrivacyManager.cPhone, className)); + } + } + return listHook; + } + + public static List getPhoneInstances() { + List listHook = new ArrayList(); + if (Hook.isAOSP(19)) { + listHook.add(new XTelephonyManager(Methods.Srv_enableLocationUpdates, PrivacyManager.cLocation, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_disableLocationUpdates, null, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getAllCellInfo, PrivacyManager.cLocation, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getCellLocation, PrivacyManager.cLocation, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getNeighboringCellInfo, PrivacyManager.cLocation, Srv.Phone)); + + listHook.add(new XTelephonyManager(Methods.Srv_enableLocationUpdatesForSubscriber, + PrivacyManager.cLocation, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_disableLocationUpdatesForSubscriber, null, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getCdmaMdn, PrivacyManager.cPhone, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getCdmaMin, PrivacyManager.cPhone, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getLine1AlphaTagForDisplay, PrivacyManager.cPhone, Srv.Phone)); + listHook.add(new XTelephonyManager(Methods.Srv_getLine1NumberForDisplay, PrivacyManager.cPhone, Srv.Phone)); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) + listHook.add(new XTelephonyManager(Methods.Srv_getDeviceId5, PrivacyManager.cPhone, Srv.Phone)); + return listHook; + } + + public static List getRegistryInstances() { + List listHook = new ArrayList(); + listHook.add(new XTelephonyManager(Methods.Srv_listen, PrivacyManager.cLocation, Srv.Registry)); + listHook.add(new XTelephonyManager(Methods.Srv_listen, PrivacyManager.cPhone, Srv.Registry)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case disableLocationUpdates: + if (isRestricted(param, PrivacyManager.cLocation, "enableLocationUpdates")) + param.setResult(null); + break; + + case Srv_disableLocationUpdates: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_enableLocationUpdates")) + param.setResult(null); + break; + + case Srv_disableLocationUpdatesForSubscriber: + if (isRestricted(param, PrivacyManager.cLocation, "Srv_enableLocationUpdatesForSubscriber")) + param.setResult(null); + break; + + case enableLocationUpdates: + case Srv_enableLocationUpdates: + case Srv_enableLocationUpdatesForSubscriber: + if (isRestricted(param)) + param.setResult(null); + break; + + case getAllCellInfo: + case getCellLocation: + case getDeviceId: + case getGroupIdLevel1: + case getIsimDomain: + case getIsimImpi: + case getIsimImpu: + case getLine1AlphaTag: + case getLine1Number: + case getMsisdn: + case getNeighboringCellInfo: + case getNetworkCountryIso: + case getNetworkOperator: + case getNetworkOperatorName: + case getSimCountryIso: + case getSimOperator: + case getSimOperatorName: + case getSimSerialNumber: + case getSubscriberId: + case getVoiceMailAlphaTag: + case getVoiceMailNumber: + case Srv_getAllCellInfo: + case Srv_getCellLocation: + case Srv_getNeighboringCellInfo: + break; + + case listen: + if (param.args.length > 1 && param.args[0] instanceof PhoneStateListener + && param.args[1] instanceof Integer) { + PhoneStateListener listener = (PhoneStateListener) param.args[0]; + int event = (Integer) param.args[1]; + if (event == PhoneStateListener.LISTEN_NONE) { + // Remove + synchronized (mListener) { + XPhoneStateListener xListener = mListener.get(listener); + if (xListener != null) { + param.args[0] = xListener; + Util.log(this, Log.WARN, + "Removed count=" + mListener.size() + " uid=" + Binder.getCallingUid()); + } + } + } else if (isRestricted(param)) + try { + // Replace + XPhoneStateListener xListener; + synchronized (mListener) { + xListener = mListener.get(listener); + if (xListener == null) { + xListener = new XPhoneStateListener(listener); + mListener.put(listener, xListener); + Util.log(this, Log.WARN, + "Added count=" + mListener.size() + " uid=" + Binder.getCallingUid()); + } + } + param.args[0] = xListener; + } catch (Throwable ignored) { + // Some implementations require a looper + // which is not according to the documentation + // and stock source code + } + } + break; + + case Srv_listen: + if (isRestricted(param)) + param.setResult(null); + break; + + case Srv_getDeviceId: + case Srv_getGroupIdLevel1: + case Srv_getIccSerialNumber: + case Srv_getIsimDomain: + case Srv_getIsimImpi: + case Srv_getIsimImpu: + case Srv_getLine1AlphaTag: + case Srv_getLine1Number: + case Srv_getMsisdn: + case Srv_getSubscriberId: + case Srv_getCompleteVoiceMailNumber: + case Srv_getVoiceMailNumber: + case Srv_getVoiceMailAlphaTag: + case Srv_getImei: + case Srv_getIsimIst: + case Srv_getIsimPcscf: + case Srv_getCdmaMdn: + case Srv_getCdmaMin: + case Srv_getLine1AlphaTagForDisplay: + case Srv_getLine1NumberForDisplay: + break; + + case Srv_getCompleteVoiceMailNumberForSubscriber5: + case Srv_getDeviceId5: + case Srv_getDeviceIdForPhone5: + case Srv_getDeviceIdForSubscriber5: + case Srv_getGroupIdLevel1ForSubscriber5: + case Srv_getIccSerialNumberForSubscriber5: + case Srv_getImeiForSubscriber5: + case Srv_getIsimDomain5: + case Srv_getIsimImpi5: + case Srv_getIsimImpu5: + case Srv_getIsimIst5: + case Srv_getIsimPcscf5: + case Srv_getLine1AlphaTagForSubscriber5: + case Srv_getLine1NumberForSubscriber5: + case Srv_getMsisdnForSubscriber5: + case Srv_getNaiForSubscriber5: + case Srv_getSubscriberIdForSubscriber5: + case Srv_getVoiceMailAlphaTagForSubscriber5: + case Srv_getVoiceMailNumberForSubscriber5: + break; + + } + } + + @Override + protected void after(XParam param) throws Throwable { + int uid = Binder.getCallingUid(); + + switch (mMethod) { + case disableLocationUpdates: + case enableLocationUpdates: + case Srv_disableLocationUpdates: + case Srv_enableLocationUpdates: + case Srv_disableLocationUpdatesForSubscriber: + case Srv_enableLocationUpdatesForSubscriber: + break; + + case getAllCellInfo: + case Srv_getAllCellInfo: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getCellLocation: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(getDefacedCellLocation(uid)); + break; + + case Srv_getCellLocation: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(getDefacedCellBundle(uid)); + break; + + case getNeighboringCellInfo: + case Srv_getNeighboringCellInfo: + if (param.getResult() != null && isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case listen: + case Srv_listen: + break; + + case getDeviceId: + case getGroupIdLevel1: + case getIsimDomain: + case getIsimImpi: + case getIsimImpu: + case getNetworkCountryIso: + case getNetworkOperator: + case getNetworkOperatorName: + case getSimCountryIso: + case getSimOperator: + case getSimOperatorName: + case getSimSerialNumber: + case getSubscriberId: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(uid, mMethod.name())); + break; + + case getLine1AlphaTag: + case getLine1Number: + case getMsisdn: + case getVoiceMailAlphaTag: + case getVoiceMailNumber: + String phoneNumber = (String) param.getResult(); + if (phoneNumber != null) + if (isRestrictedValue(param, phoneNumber)) + param.setResult(PrivacyManager.getDefacedProp(uid, mMethod.name())); + break; + + case Srv_getDeviceId: + case Srv_getGroupIdLevel1: + case Srv_getIccSerialNumber: + case Srv_getIsimDomain: + case Srv_getIsimImpi: + case Srv_getIsimImpu: + case Srv_getSubscriberId: + case Srv_getImei: + case Srv_getDeviceId5: + case Srv_getDeviceIdForPhone5: + case Srv_getDeviceIdForSubscriber5: + case Srv_getGroupIdLevel1ForSubscriber5: + case Srv_getIccSerialNumberForSubscriber5: + case Srv_getImeiForSubscriber5: + case Srv_getIsimDomain5: + case Srv_getIsimImpi5: + case Srv_getIsimImpu5: + case Srv_getNaiForSubscriber5: + case Srv_getSubscriberIdForSubscriber5: + if (param.getResult() != null) + if (isRestricted(param)) { + String name = mMethod.name(); + name = name.replace("Srv_", ""); + name = name.replace("ForPhone", ""); + name = name.replace("ForSubscriber", ""); + name = name.replace("5", ""); + param.setResult(PrivacyManager.getDefacedProp(uid, name)); + } + break; + + case Srv_getLine1AlphaTag: + case Srv_getLine1Number: + case Srv_getMsisdn: + case Srv_getCompleteVoiceMailNumber: + case Srv_getVoiceMailNumber: + case Srv_getVoiceMailAlphaTag: + case Srv_getLine1AlphaTagForDisplay: + case Srv_getLine1NumberForDisplay: + case Srv_getCompleteVoiceMailNumberForSubscriber5: + case Srv_getLine1AlphaTagForSubscriber5: + case Srv_getLine1NumberForSubscriber5: + case Srv_getMsisdnForSubscriber5: + case Srv_getVoiceMailAlphaTagForSubscriber5: + case Srv_getVoiceMailNumberForSubscriber5: + String srvPhoneNumber = (String) param.getResult(); + if (srvPhoneNumber != null) + if (isRestrictedValue(param, srvPhoneNumber)) { + String name = mMethod.name(); + name = name.replace("Srv_", ""); + name = name.replace("ForSubscriber", ""); + name = name.replace("5", ""); + param.setResult(PrivacyManager.getDefacedProp(uid, name)); + } + break; + + case Srv_getIsimIst: + case Srv_getIsimPcscf: + case Srv_getCdmaMdn: + case Srv_getCdmaMin: + case Srv_getIsimIst5: + case Srv_getIsimPcscf5: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(null); + break; + } + } + + private static CellLocation getDefacedCellLocation(int uid) { + int cid = (Integer) PrivacyManager.getDefacedProp(uid, "CID"); + int lac = (Integer) PrivacyManager.getDefacedProp(uid, "LAC"); + if (cid > 0 && lac > 0) { + GsmCellLocation cellLocation = new GsmCellLocation(); + cellLocation.setLacAndCid(lac, cid); + return cellLocation; + } else + return CellLocation.getEmpty(); + } + + private static Bundle getDefacedCellBundle(int uid) { + Bundle result = new Bundle(); + int cid = (Integer) PrivacyManager.getDefacedProp(uid, "CID"); + int lac = (Integer) PrivacyManager.getDefacedProp(uid, "LAC"); + if (cid > 0 && lac > 0) { + result.putInt("lac", lac); + result.putInt("cid", cid); + } + return result; + } + + private class XPhoneStateListener extends PhoneStateListener { + private PhoneStateListener mListener; + + public XPhoneStateListener(PhoneStateListener listener) { + mListener = listener; + } + + @Override + public void onCallForwardingIndicatorChanged(boolean cfi) { + mListener.onCallForwardingIndicatorChanged(cfi); + } + + @Override + public void onCallStateChanged(int state, String incomingNumber) { + mListener.onCallStateChanged(state, + (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "PhoneNumber")); + } + + @Override + public void onCellInfoChanged(List cellInfo) { + mListener.onCellInfoChanged(new ArrayList()); + } + + @Override + public void onCellLocationChanged(CellLocation location) { + mListener.onCellLocationChanged(getDefacedCellLocation(Binder.getCallingUid())); + } + + @Override + public void onDataActivity(int direction) { + mListener.onDataActivity(direction); + } + + @Override + public void onDataConnectionStateChanged(int state) { + mListener.onDataConnectionStateChanged(state); + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + mListener.onDataConnectionStateChanged(state, networkType); + } + + @Override + public void onMessageWaitingIndicatorChanged(boolean mwi) { + mListener.onMessageWaitingIndicatorChanged(mwi); + } + + @Override + public void onServiceStateChanged(ServiceState serviceState) { + mListener.onServiceStateChanged(serviceState); + } + + @Override + @SuppressWarnings("deprecation") + public void onSignalStrengthChanged(int asu) { + mListener.onSignalStrengthChanged(asu); + } + + @Override + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + mListener.onSignalStrengthsChanged(signalStrength); + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUsageStatsManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUsageStatsManager.java new file mode 100644 index 0000000..b6a955f --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUsageStatsManager.java @@ -0,0 +1,118 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import android.app.usage.ConfigurationStats; +import android.app.usage.UsageStats; + +import biz.bokhorst.xprivacy.XHook; + +public class XUsageStatsManager extends XHook { + private Methods mMethod; + + private XUsageStatsManager(Methods method, String restrictionName) { + super(restrictionName, method.name().replace("Srv_", ""), method.name()); + mMethod = method; + } + + @Override + public boolean isVisible() { + return !mMethod.name().startsWith("Srv_"); + } + + public String getClassName() { + if (mMethod.name().startsWith("Srv_")) + return "com.android.server.usage.UserUsageStatsService"; + else + return "android.app.usage.UsageStatsManager"; + } + + // @formatter:off + + // public Map queryAndAggregateUsageStats(long beginTime, long endTime) + // public List queryConfigurations(int intervalType, long beginTime, long endTime) + // public UsageEvents queryEvents(long beginTime, long endTime) + // public List queryUsageStats(int intervalType, long beginTime, long endTime) + // https://developer.android.com/reference/android/app/usage/UsageStatsManager.html + + // List queryConfigurationStats(int userId, int bucketType, long beginTime, long endTime) + // UsageEvents queryEvents(int userId, long beginTime, long endTime) + // List queryUsageStats(int userId, int bucketType, long beginTime, long endTime) + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/usage/UsageStatsService.java + + private enum Methods { + queryAndAggregateUsageStats, queryConfigurations, queryEvents, queryUsageStats, + Srv_queryConfigurationStats, Srv_queryEvents, Srv_queryUsageStats + }; + + // @formatter:on + + public static List getInstances(boolean server) { + List listHook = new ArrayList(); + if (server) { + listHook.add(new XUsageStatsManager(Methods.Srv_queryConfigurationStats, PrivacyManager.cSystem)); + listHook.add(new XUsageStatsManager(Methods.Srv_queryEvents, PrivacyManager.cSystem)); + listHook.add(new XUsageStatsManager(Methods.Srv_queryUsageStats, PrivacyManager.cSystem)); + } else { + listHook.add(new XUsageStatsManager(Methods.queryAndAggregateUsageStats, PrivacyManager.cSystem)); + listHook.add(new XUsageStatsManager(Methods.queryConfigurations, PrivacyManager.cSystem)); + listHook.add(new XUsageStatsManager(Methods.queryEvents, PrivacyManager.cSystem)); + listHook.add(new XUsageStatsManager(Methods.queryUsageStats, PrivacyManager.cSystem)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case queryAndAggregateUsageStats: + case queryConfigurations: + case queryUsageStats: + case Srv_queryConfigurationStats: + case Srv_queryUsageStats: + // Do nothing + break; + case queryEvents: + if (isRestricted(param)) + if (param.args.length > 1) { + param.args[0] = 0; + param.args[1] = 0; + } + break; + + case Srv_queryEvents: + if (isRestricted(param)) + if (param.args.length > 2) { + param.args[1] = 0; + param.args[2] = 0; + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case queryAndAggregateUsageStats: + if (isRestricted(param)) + param.setResult(new HashMap()); + break; + case queryConfigurations: + case Srv_queryConfigurationStats: + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + case queryEvents: + case Srv_queryEvents: + // Do nothing + break; + case queryUsageStats: + case Srv_queryUsageStats: + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUsbDevice.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUsbDevice.java new file mode 100644 index 0000000..2bf44eb --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUsbDevice.java @@ -0,0 +1,65 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import biz.bokhorst.xprivacy.XHook; + +public class XUsbDevice extends XHook { + private Methods mMethod; + + private XUsbDevice(Methods method, String restrictionName) { + super(restrictionName, method.name(), "USB." + method.name()); + mMethod = method; + } + + public String getClassName() { + return "android.hardware.usb.UsbDevice"; + } + + // public static int getDeviceId(String name) + // public int getDeviceId() + // public String getDeviceName() + // public static String getDeviceName(int id) + // public String getSerialNumber() + // http://developer.android.com/reference/android/hardware/usb/UsbDevice.html + + private enum Methods { + getDeviceId, getDeviceName, getSerialNumber + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XUsbDevice(Methods.getDeviceId, PrivacyManager.cIdentification)); + listHook.add(new XUsbDevice(Methods.getDeviceName, PrivacyManager.cIdentification)); + listHook.add(new XUsbDevice(Methods.getSerialNumber, PrivacyManager.cIdentification)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case getDeviceId: + if (param.args.length > 0 && param.args[0] instanceof String) { + if (isRestrictedExtra(param, (String) param.args[0])) + param.setResult(0); + } else { + if (isRestricted(param)) + param.setResult(0); + } + break; + + case getDeviceName: + case getSerialNumber: + if (param.getResult() != null && isRestricted(param)) + param.setResult(PrivacyManager.getDefacedProp(Binder.getCallingUid(), "USB")); + break; + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUtilHook.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUtilHook.java new file mode 100644 index 0000000..ebea4a2 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XUtilHook.java @@ -0,0 +1,35 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.util.Log; + +public class XUtilHook extends XHook { + + private XUtilHook(String methodName, String restrictionName) { + super(restrictionName, methodName, null); + } + + public String getClassName() { + return Util.class.getName(); + } + + // isXposedEnabled + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XUtilHook("isXposedEnabled", null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + Util.log(this, Log.INFO, param.method.getName() + "=true"); + param.setResult(true); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWebSettings.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWebSettings.java new file mode 100644 index 0000000..2d618bc --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWebSettings.java @@ -0,0 +1,92 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.util.Log; + +public class XWebSettings extends XHook { + private Methods mMethod; + private String mClassName; + + private XWebSettings(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // public static String getDefaultUserAgent(Context context) [17] + // public synchronized int getUserAgent() + // public synchronized String getUserAgentString() + // public synchronized void setUserAgent(int ua) + // public synchronized void setUserAgentString (String ua) + // http://developer.android.com/reference/android/webkit/WebSettings.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/webkit/WebSettings.java + + private enum Methods { + getDefaultUserAgent, getUserAgent, getUserAgentString, setUserAgent, setUserAgentString + }; + + public static List getInstances(Object instance) { + String className = instance.getClass().getName(); + Util.log(null, Log.INFO, "Hooking class=" + className + " uid=" + Binder.getCallingUid()); + + List listHook = new ArrayList(); + listHook.add(new XWebSettings(Methods.getDefaultUserAgent, PrivacyManager.cView, className)); + listHook.add(new XWebSettings(Methods.getUserAgent, PrivacyManager.cView, className)); + listHook.add(new XWebSettings(Methods.getUserAgentString, PrivacyManager.cView, className)); + listHook.add(new XWebSettings(Methods.setUserAgent, PrivacyManager.cView, className)); + listHook.add(new XWebSettings(Methods.setUserAgentString, PrivacyManager.cView, className)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case getDefaultUserAgent: + int uid = Binder.getCallingUid(); + if (getRestricted(uid)) { + String ua = (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "UA"); + param.setResult(ua); + } + break; + + case getUserAgent: + if (isRestricted(param)) + param.setResult(-1); // User defined + break; + + case getUserAgentString: + if (isRestrictedExtra(param, (String) param.getResult())) { + String ua = (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "UA"); + param.setResult(ua); + } + break; + + case setUserAgent: + if (param.args.length > 0) + if (isRestricted(param)) + param.args[0] = -1; // User defined + break; + + case setUserAgentString: + if (param.args.length > 0) + if (isRestricted(param)) { + String ua = (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "UA"); + param.args[0] = ua; + } + break; + + } + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWebView.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWebView.java new file mode 100644 index 0000000..a129808 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWebView.java @@ -0,0 +1,103 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Binder; +import android.webkit.WebView; + +public class XWebView extends XHook { + private Methods mMethod; + + private XWebView(Methods method, String restrictionName) { + super(restrictionName, (method == Methods.WebView ? null : method.name()), (method == Methods.WebView ? method + .name() : null)); + mMethod = method; + } + + public String getClassName() { + return "android.webkit.WebView"; + } + + // @formatter:off + + // public WebView(Context context) + // public WebView(Context context, AttributeSet attrs) + // public WebView(Context context, AttributeSet attrs, int defStyle) + // public WebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) + // public WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) + // protected WebView(Context context, AttributeSet attrs, int defStyle, Map javaScriptInterfaces, boolean privateBrowsing) + // public WebSettings getSettings() + // public void loadUrl(String url) + // public void loadUrl(String url, Map additionalHttpHeaders) + // public postUrl(String url, byte[] postData) + // http://developer.android.com/reference/android/webkit/WebView.html + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/webkit/WebView.java/ + + // @formatter:on + + private enum Methods { + WebView, loadUrl, postUrl, getSettings + }; + + public static List getInstances() { + List listHook = new ArrayList(); + listHook.add(new XWebView(Methods.WebView, null)); + listHook.add(new XWebView(Methods.loadUrl, PrivacyManager.cView)); + listHook.add(new XWebView(Methods.postUrl, PrivacyManager.cView)); + listHook.add(new XWebView(Methods.getSettings, null)); + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + switch (mMethod) { + case WebView: + case getSettings: + // Do nothing + break; + + case loadUrl: + case postUrl: + if (param.args.length > 0 && param.thisObject instanceof WebView) { + String extra = (param.args[0] instanceof String ? (String) param.args[0] : null); + if (isRestrictedExtra(param, extra)) + param.setResult(null); + } + break; + } + } + + @Override + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case WebView: + if (param.args.length > 0 && param.thisObject instanceof WebView) { + if (isRestricted(param, PrivacyManager.cView, "initUserAgentString")) { + String ua = (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "UA"); + WebView webView = (WebView) param.thisObject; + if (webView.getSettings() != null) + webView.getSettings().setUserAgentString(ua); + } + } + break; + + case loadUrl: + case postUrl: + // Do nothing + break; + + case getSettings: + if (param.getResult() != null) { + Class clazz = param.getResult().getClass(); + if (PrivacyManager.getTransient(clazz.getName(), null) == null) { + PrivacyManager.setTransient(clazz.getName(), Boolean.toString(true)); + XPrivacy.hookAll(XWebSettings.getInstances(param.getResult()), clazz.getClassLoader(), getSecret(), + true); + } + } + break; + + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWifiManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWifiManager.java new file mode 100644 index 0000000..e71e3ff --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWifiManager.java @@ -0,0 +1,218 @@ +package biz.bokhorst.xprivacy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import android.net.DhcpInfo; +import android.net.wifi.ScanResult; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.os.Binder; +import android.os.Build; + +public class XWifiManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.net.wifi.WifiManager"; + + private XWifiManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name().replace("Srv_", ""), "WiFi." + method.name()); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public List getConfiguredNetworks() + // public WifiInfo getConnectionInfo() + // public DhcpInfo getDhcpInfo() + // public List getScanResults() + // public WifiConfiguration getWifiApConfiguration() + // frameworks/base/wifi/java/android/net/wifi/WifiManager.java + // frameworks/base/wifi/java/android/net/wifi/WifiInfo.java + // frameworks/base/core/java/android/net/DhcpInfo.java + // http://developer.android.com/reference/android/net/wifi/WifiManager.html + + // public java.util.List getBatchedScanResults(java.lang.String callingPackage) + // public java.util.List getConfiguredNetworks() + // public android.net.wifi.WifiInfo getConnectionInfo() + // public android.net.DhcpInfo getDhcpInfo() + // public java.util.List getScanResults(java.lang.String callingPackage) + // public android.net.wifi.WifiConfiguration getWifiApConfiguration() + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/com/android/server/wifi/WifiService.java + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/com/android/server/wifi/WifiServiceImpl.java + + // @formatter:on + + // @formatter:off + private enum Methods { + getConfiguredNetworks, getConnectionInfo, getDhcpInfo, getScanResults, getWifiApConfiguration, + Srv_getBatchedScanResults, Srv_getConfiguredNetworks, Srv_getConnectionInfo, Srv_getDhcpInfo, Srv_getScanResults, Srv_getWifiApConfiguration + }; + // @formatter:on + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + String srvClassName; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + srvClassName = "com.android.server.wifi.WifiServiceImpl"; + else + srvClassName = "com.android.server.wifi.WifiService"; + + for (Methods wifi : Methods.values()) + if (wifi.name().startsWith("Srv_")) { + if (server) + listHook.add(new XWifiManager(wifi, PrivacyManager.cNetwork, srvClassName)); + } else + listHook.add(new XWifiManager(wifi, PrivacyManager.cNetwork, className)); + + listHook.add(new XWifiManager(Methods.getScanResults, PrivacyManager.cLocation, className)); + if (server) + listHook.add(new XWifiManager(Methods.Srv_getScanResults, PrivacyManager.cLocation, srvClassName)); + + // This is to fake "offline", no permission required + listHook.add(new XWifiManager(Methods.getConnectionInfo, PrivacyManager.cInternet, className)); + if (server) + listHook.add(new XWifiManager(Methods.Srv_getConnectionInfo, PrivacyManager.cInternet, srvClassName)); + } + + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + // Do nothing + } + + @Override + @SuppressWarnings("rawtypes") + protected void after(XParam param) throws Throwable { + switch (mMethod) { + case Srv_getBatchedScanResults: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getConfiguredNetworks: + case Srv_getConfiguredNetworks: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getConnectionInfo: + case Srv_getConnectionInfo: + if (param.getResult() != null) + if (isRestricted(param)) { + WifiInfo result = (WifiInfo) param.getResult(); + WifiInfo wInfo = WifiInfo.class.getConstructor(WifiInfo.class).newInstance(result); + if (getRestrictionName().equals(PrivacyManager.cInternet)) { + // Supplicant state + try { + Field fieldState = WifiInfo.class.getDeclaredField("mSupplicantState"); + fieldState.setAccessible(true); + fieldState.set(wInfo, SupplicantState.DISCONNECTED); + } catch (Throwable ex) { + Util.bug(this, ex); + } + + } else { + // BSSID + try { + Field fieldBSSID = WifiInfo.class.getDeclaredField("mBSSID"); + fieldBSSID.setAccessible(true); + fieldBSSID.set(wInfo, PrivacyManager.getDefacedProp(Binder.getCallingUid(), "MAC")); + } catch (Throwable ex) { + Util.bug(this, ex); + } + + // IP address + try { + Field fieldIp = WifiInfo.class.getDeclaredField("mIpAddress"); + fieldIp.setAccessible(true); + fieldIp.set(wInfo, PrivacyManager.getDefacedProp(Binder.getCallingUid(), "InetAddress")); + } catch (Throwable ex) { + Util.bug(this, ex); + } + + // MAC address + try { + Field fieldMAC = WifiInfo.class.getDeclaredField("mMacAddress"); + fieldMAC.setAccessible(true); + fieldMAC.set(wInfo, PrivacyManager.getDefacedProp(Binder.getCallingUid(), "MAC")); + } catch (Throwable ex) { + Util.bug(this, ex); + } + + // SSID + String ssid = (String) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "SSID"); + try { + Field fieldSSID = WifiInfo.class.getDeclaredField("mSSID"); + fieldSSID.setAccessible(true); + fieldSSID.set(wInfo, ssid); + } catch (Throwable ex) { + try { + Field fieldWifiSsid = WifiInfo.class.getDeclaredField("mWifiSsid"); + fieldWifiSsid.setAccessible(true); + Object mWifiSsid = fieldWifiSsid.get(wInfo); + if (mWifiSsid != null) { + // public static WifiSsid + // createFromAsciiEncoded(String + // asciiEncoded) + Method methodCreateFromAsciiEncoded = mWifiSsid.getClass().getDeclaredMethod( + "createFromAsciiEncoded", String.class); + fieldWifiSsid.set(wInfo, methodCreateFromAsciiEncoded.invoke(null, ssid)); + } + } catch (Throwable exex) { + Util.bug(this, exex); + } + } + } + param.setResult(wInfo); + } + break; + + case getDhcpInfo: + case Srv_getDhcpInfo: + if (param.getResult() != null) + if (isRestricted(param)) { + DhcpInfo result = (DhcpInfo) param.getResult(); + DhcpInfo dInfo = DhcpInfo.class.getConstructor(DhcpInfo.class).newInstance(result); + dInfo.ipAddress = (Integer) PrivacyManager.getDefacedProp(Binder.getCallingUid(), "IPInt"); + dInfo.gateway = dInfo.ipAddress; + dInfo.dns1 = dInfo.ipAddress; + dInfo.dns2 = dInfo.ipAddress; + dInfo.serverAddress = dInfo.ipAddress; + param.setResult(dInfo); + } + break; + + case getScanResults: + case Srv_getScanResults: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(new ArrayList()); + break; + + case getWifiApConfiguration: + case Srv_getWifiApConfiguration: + if (param.getResult() != null) + if (isRestricted(param)) + param.setResult(null); + break; + + } + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWindowManager.java b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWindowManager.java new file mode 100644 index 0000000..4ccbdf2 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/biz/bokhorst/xprivacy/XWindowManager.java @@ -0,0 +1,91 @@ +package biz.bokhorst.xprivacy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import android.util.Log; +import android.view.View; +import android.view.WindowManager; + +public class XWindowManager extends XHook { + private Methods mMethod; + private String mClassName; + private static final String cClassName = "android.view.WindowManagerImpl"; + private static final Map mViewParam = new WeakHashMap(); + + private XWindowManager(Methods method, String restrictionName, String className) { + super(restrictionName, method.name(), null); + mMethod = method; + mClassName = className; + } + + public String getClassName() { + return mClassName; + } + + // @formatter:off + + // public void addView(View view, ViewGroup.LayoutParams params) + // public void removeView(View view) + // public void updateViewLayout(View view, ViewGroup.LayoutParams params) + // http://developer.android.com/reference/android/view/ViewManager.html + // http://developer.android.com/reference/android/view/WindowManager.html + + // @formatter:on + + private enum Methods { + addView, removeView, updateViewLayout + }; + + public static List getInstances(String className, boolean server) { + List listHook = new ArrayList(); + if (!cClassName.equals(className)) { + if (className == null) + className = cClassName; + + listHook.add(new XWindowManager(Methods.addView, PrivacyManager.cOverlay, className)); + listHook.add(new XWindowManager(Methods.removeView, null, className)); + listHook.add(new XWindowManager(Methods.updateViewLayout, null, className)); + } + return listHook; + } + + @Override + protected void before(XParam param) throws Throwable { + if (mMethod == Methods.addView || mMethod == Methods.removeView || mMethod == Methods.updateViewLayout) { + View view = (View) param.args[0]; + if (view != null) { + // Get params + WindowManager.LayoutParams wmParams = null; + synchronized (mViewParam) { + if (param.args.length > 1) { + wmParams = (WindowManager.LayoutParams) param.args[1]; + if (wmParams != null) + mViewParam.put(view, wmParams); + } else if (mViewParam.containsKey(view)) + wmParams = mViewParam.get(view); + } + + // Check for system alert/overlay + if (wmParams != null) + if (wmParams.type == WindowManager.LayoutParams.TYPE_SYSTEM_ALERT + || wmParams.type == WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY) + if (mMethod == Methods.removeView || mMethod == Methods.updateViewLayout) { + if (isRestricted(param, PrivacyManager.cOverlay, "addView")) + param.setResult(null); + } + + else if (isRestricted(param)) + param.setResult(null); + } + } else + Util.log(this, Log.WARN, "Unknown method=" + param.method.getName()); + } + + @Override + protected void after(XParam param) throws Throwable { + // Do nothing + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/com/android/internal/util/FastXmlSerializer.java b/src/XPrivacy-master/XPrivacy-master/src/com/android/internal/util/FastXmlSerializer.java new file mode 100644 index 0000000..7a04080 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/com/android/internal/util/FastXmlSerializer.java @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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.android.internal.util; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; + +/** + * This is a quick and dirty implementation of XmlSerializer that isn't horribly + * painfully slow like the normal one. It only does what is needed for the + * specific XML files being written with it. + */ +public class FastXmlSerializer implements XmlSerializer { + private static final String ESCAPE_TABLE[] = new String[] { + null, null, null, null, null, null, null, null, // 0-7 + null, null, null, null, null, null, null, null, // 8-15 + null, null, null, null, null, null, null, null, // 16-23 + null, null, null, null, null, null, null, null, // 24-31 + null, null, """, null, null, null, "&", null, // 32-39 + null, null, null, null, null, null, null, null, // 40-47 + null, null, null, null, null, null, null, null, // 48-55 + null, null, null, null, "<", null, ">", null, // 56-63 + }; + + private static final int BUFFER_LEN = 8192; + + private static String sSpace = " "; + + private final char[] mText = new char[BUFFER_LEN]; + private int mPos; + + private Writer mWriter; + + private OutputStream mOutputStream; + private CharsetEncoder mCharset; + private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); + + private boolean mIndent = false; + private boolean mInTag; + + private int mNesting = 0; + private boolean mLineStart = true; + + private void append(char c) throws IOException { + int pos = mPos; + if (pos >= (BUFFER_LEN-1)) { + flush(); + pos = mPos; + } + mText[pos] = c; + mPos = pos+1; + } + + private void append(String str, int i, final int length) throws IOException { + if (length > BUFFER_LEN) { + final int end = i + length; + while (i < end) { + int next = i + BUFFER_LEN; + append(str, i, next BUFFER_LEN) { + flush(); + pos = mPos; + } + str.getChars(i, i+length, mText, pos); + mPos = pos + length; + } + + private void append(char[] buf, int i, final int length) throws IOException { + if (length > BUFFER_LEN) { + final int end = i + length; + while (i < end) { + int next = i + BUFFER_LEN; + append(buf, i, next BUFFER_LEN) { + flush(); + pos = mPos; + } + System.arraycopy(buf, i, mText, pos, length); + mPos = pos + length; + } + + private void append(String str) throws IOException { + append(str, 0, str.length()); + } + + private void appendIndent(int indent) throws IOException { + indent *= 4; + if (indent > sSpace.length()) { + indent = sSpace.length(); + } + append(sSpace, 0, indent); + } + + private void escapeAndAppendString(final String string) throws IOException { + final int N = string.length(); + final char NE = (char)ESCAPE_TABLE.length; + final String[] escapes = ESCAPE_TABLE; + int lastPos = 0; + int pos; + for (pos=0; pos= NE) continue; + String escape = escapes[c]; + if (escape == null) continue; + if (lastPos < pos) append(string, lastPos, pos-lastPos); + lastPos = pos + 1; + append(escape); + } + if (lastPos < pos) append(string, lastPos, pos-lastPos); + } + + private void escapeAndAppendString(char[] buf, int start, int len) throws IOException { + final char NE = (char)ESCAPE_TABLE.length; + final String[] escapes = ESCAPE_TABLE; + int end = start+len; + int lastPos = start; + int pos; + for (pos=start; pos= NE) continue; + String escape = escapes[c]; + if (escape == null) continue; + if (lastPos < pos) append(buf, lastPos, pos-lastPos); + lastPos = pos + 1; + append(escape); + } + if (lastPos < pos) append(buf, lastPos, pos-lastPos); + } + + public XmlSerializer attribute(String namespace, String name, String value) throws IOException, + IllegalArgumentException, IllegalStateException { + append(' '); + if (namespace != null) { + append(namespace); + append(':'); + } + append(name); + append("=\""); + + escapeAndAppendString(value); + append('"'); + mLineStart = false; + return this; + } + + public void cdsect(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void comment(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void docdecl(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException { + flush(); + } + + public XmlSerializer endTag(String namespace, String name) throws IOException, + IllegalArgumentException, IllegalStateException { + mNesting--; + if (mInTag) { + append(" />\n"); + } else { + if (mIndent && mLineStart) { + appendIndent(mNesting); + } + append("\n"); + } + mLineStart = true; + mInTag = false; + return this; + } + + public void entityRef(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + private void flushBytes() throws IOException { + int position; + if ((position = mBytes.position()) > 0) { + mBytes.flip(); + mOutputStream.write(mBytes.array(), 0, position); + mBytes.clear(); + } + } + + public void flush() throws IOException { + //Log.i("PackageManager", "flush mPos=" + mPos); + if (mPos > 0) { + if (mOutputStream != null) { + CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); + CoderResult result = mCharset.encode(charBuffer, mBytes, true); + while (true) { + if (result.isError()) { + throw new IOException(result.toString()); + } else if (result.isOverflow()) { + flushBytes(); + result = mCharset.encode(charBuffer, mBytes, true); + continue; + } + break; + } + flushBytes(); + mOutputStream.flush(); + } else { + mWriter.write(mText, 0, mPos); + mWriter.flush(); + } + mPos = 0; + } + } + + public int getDepth() { + throw new UnsupportedOperationException(); + } + + public boolean getFeature(String name) { + throw new UnsupportedOperationException(); + } + + public String getName() { + throw new UnsupportedOperationException(); + } + + public String getNamespace() { + throw new UnsupportedOperationException(); + } + + public String getPrefix(String namespace, boolean generatePrefix) + throws IllegalArgumentException { + throw new UnsupportedOperationException(); + } + + public Object getProperty(String name) { + throw new UnsupportedOperationException(); + } + + public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void processingInstruction(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void setFeature(String name, boolean state) throws IllegalArgumentException, + IllegalStateException { + if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) { + mIndent = true; + return; + } + throw new UnsupportedOperationException(); + } + + public void setOutput(OutputStream os, String encoding) throws IOException, + IllegalArgumentException, IllegalStateException { + if (os == null) + throw new IllegalArgumentException(); + if (true) { + try { + mCharset = Charset.forName(encoding).newEncoder(); + } catch (IllegalCharsetNameException e) { + throw (UnsupportedEncodingException) (new UnsupportedEncodingException( + encoding).initCause(e)); + } catch (UnsupportedCharsetException e) { + throw (UnsupportedEncodingException) (new UnsupportedEncodingException( + encoding).initCause(e)); + } + mOutputStream = os; + } else { + setOutput( + encoding == null + ? new OutputStreamWriter(os) + : new OutputStreamWriter(os, encoding)); + } + } + + public void setOutput(Writer writer) throws IOException, IllegalArgumentException, + IllegalStateException { + mWriter = writer; + } + + public void setPrefix(String prefix, String namespace) throws IOException, + IllegalArgumentException, IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void setProperty(String name, Object value) throws IllegalArgumentException, + IllegalStateException { + throw new UnsupportedOperationException(); + } + + public void startDocument(String encoding, Boolean standalone) throws IOException, + IllegalArgumentException, IllegalStateException { + append("\n"); + mLineStart = true; + } + + public XmlSerializer startTag(String namespace, String name) throws IOException, + IllegalArgumentException, IllegalStateException { + if (mInTag) { + append(">\n"); + } + if (mIndent) { + appendIndent(mNesting); + } + mNesting++; + append('<'); + if (namespace != null) { + append(namespace); + append(':'); + } + append(name); + mInTag = true; + mLineStart = false; + return this; + } + + public XmlSerializer text(char[] buf, int start, int len) throws IOException, + IllegalArgumentException, IllegalStateException { + if (mInTag) { + append(">"); + mInTag = false; + } + escapeAndAppendString(buf, start, len); + if (mIndent) { + mLineStart = buf[start+len-1] == '\n'; + } + return this; + } + + public XmlSerializer text(String text) throws IOException, IllegalArgumentException, + IllegalStateException { + if (mInTag) { + append(">"); + mInTag = false; + } + escapeAndAppendString(text); + if (mIndent) { + mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n'); + } + return this; + } + +} diff --git a/src/XPrivacy-master/XPrivacy-master/src/com/android/internal/util/XmlUtils.java b/src/XPrivacy-master/XPrivacy-master/src/com/android/internal/util/XmlUtils.java new file mode 100644 index 0000000..0b74cf3 --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/src/com/android/internal/util/XmlUtils.java @@ -0,0 +1,966 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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.android.internal.util; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** {@hide} */ +public class XmlUtils { + + public static void skipCurrentTag(XmlPullParser parser) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + } + } + + public static final int + convertValueToList(CharSequence value, String[] options, int defaultValue) + { + if (null != value) { + for (int i = 0; i < options.length; i++) { + if (value.equals(options[i])) + return i; + } + } + + return defaultValue; + } + + public static final boolean + convertValueToBoolean(CharSequence value, boolean defaultValue) + { + boolean result = false; + + if (null == value) + return defaultValue; + + if (value.equals("1") + || value.equals("true") + || value.equals("TRUE")) + result = true; + + return result; + } + + public static final int + convertValueToInt(CharSequence charSeq, int defaultValue) + { + if (null == charSeq) + return defaultValue; + + String nm = charSeq.toString(); + + // XXX This code is copied from Integer.decode() so we don't + // have to instantiate an Integer! + + int value; + int sign = 1; + int index = 0; + int len = nm.length(); + int base = 10; + + if ('-' == nm.charAt(0)) { + sign = -1; + index++; + } + + if ('0' == nm.charAt(index)) { + // Quick check for a zero by itself + if (index == (len - 1)) + return 0; + + char c = nm.charAt(index + 1); + + if ('x' == c || 'X' == c) { + index += 2; + base = 16; + } else { + index++; + base = 8; + } + } + else if ('#' == nm.charAt(index)) + { + index++; + base = 16; + } + + return Integer.parseInt(nm.substring(index), base) * sign; + } + + public static int convertValueToUnsignedInt(String value, int defaultValue) { + if (null == value) { + return defaultValue; + } + + return parseUnsignedIntAttribute(value); + } + + public static int parseUnsignedIntAttribute(CharSequence charSeq) { + String value = charSeq.toString(); + + long bits; + int index = 0; + int len = value.length(); + int base = 10; + + if ('0' == value.charAt(index)) { + // Quick check for zero by itself + if (index == (len - 1)) + return 0; + + char c = value.charAt(index + 1); + + if ('x' == c || 'X' == c) { // check for hex + index += 2; + base = 16; + } else { // check for octal + index++; + base = 8; + } + } else if ('#' == value.charAt(index)) { + index++; + base = 16; + } + + return (int) Long.parseLong(value.substring(index), base); + } + + /** + * Flatten a Map into an output stream as XML. The map can later be + * read back with readMapXml(). + * + * @param val The map to be flattened. + * @param out Where to write the XML data. + * + * @see #writeMapXml(Map, String, XmlSerializer) + * @see #writeListXml + * @see #writeValueXml + * @see #readMapXml + */ + public static final void writeMapXml(Map val, OutputStream out) + throws XmlPullParserException, java.io.IOException { + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(out, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + writeMapXml(val, null, serializer); + serializer.endDocument(); + } + + /** + * Flatten a List into an output stream as XML. The list can later be + * read back with readListXml(). + * + * @param val The list to be flattened. + * @param out Where to write the XML data. + * + * @see #writeListXml(List, String, XmlSerializer) + * @see #writeMapXml + * @see #writeValueXml + * @see #readListXml + */ + public static final void writeListXml(List val, OutputStream out) + throws XmlPullParserException, java.io.IOException + { + XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(out, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + writeListXml(val, null, serializer); + serializer.endDocument(); + } + + /** + * Flatten a Map into an XmlSerializer. The map can later be read back + * with readThisMapXml(). + * + * @param val The map to be flattened. + * @param name Name attribute to include with this list's tag, or null for + * none. + * @param out XmlSerializer to write the map into. + * + * @see #writeMapXml(Map, OutputStream) + * @see #writeListXml + * @see #writeValueXml + * @see #readMapXml + */ + public static final void writeMapXml(Map val, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException + { + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + Set s = val.entrySet(); + Iterator i = s.iterator(); + + out.startTag(null, "map"); + if (name != null) { + out.attribute(null, "name", name); + } + + while (i.hasNext()) { + Map.Entry e = (Map.Entry)i.next(); + writeValueXml(e.getValue(), (String)e.getKey(), out); + } + + out.endTag(null, "map"); + } + + /** + * Flatten a List into an XmlSerializer. The list can later be read back + * with readThisListXml(). + * + * @param val The list to be flattened. + * @param name Name attribute to include with this list's tag, or null for + * none. + * @param out XmlSerializer to write the list into. + * + * @see #writeListXml(List, OutputStream) + * @see #writeMapXml + * @see #writeValueXml + * @see #readListXml + */ + public static final void writeListXml(List val, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException + { + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "list"); + if (name != null) { + out.attribute(null, "name", name); + } + + int N = val.size(); + int i=0; + while (i < N) { + writeValueXml(val.get(i), null, out); + i++; + } + + out.endTag(null, "list"); + } + + public static final void writeSetXml(Set val, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException { + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "set"); + if (name != null) { + out.attribute(null, "name", name); + } + + for (Object v : val) { + writeValueXml(v, null, out); + } + + out.endTag(null, "set"); + } + + /** + * Flatten a byte[] into an XmlSerializer. The list can later be read back + * with readThisByteArrayXml(). + * + * @param val The byte array to be flattened. + * @param name Name attribute to include with this array's tag, or null for + * none. + * @param out XmlSerializer to write the array into. + * + * @see #writeMapXml + * @see #writeValueXml + */ + public static final void writeByteArrayXml(byte[] val, String name, + XmlSerializer out) + throws XmlPullParserException, java.io.IOException { + + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "byte-array"); + if (name != null) { + out.attribute(null, "name", name); + } + + final int N = val.length; + out.attribute(null, "num", Integer.toString(N)); + + StringBuilder sb = new StringBuilder(val.length*2); + for (int i=0; i>4; + sb.append(h >= 10 ? ('a'+h-10) : ('0'+h)); + h = b&0xff; + sb.append(h >= 10 ? ('a'+h-10) : ('0'+h)); + } + + out.text(sb.toString()); + + out.endTag(null, "byte-array"); + } + + /** + * Flatten an int[] into an XmlSerializer. The list can later be read back + * with readThisIntArrayXml(). + * + * @param val The int array to be flattened. + * @param name Name attribute to include with this array's tag, or null for + * none. + * @param out XmlSerializer to write the array into. + * + * @see #writeMapXml + * @see #writeValueXml + * @see #readThisIntArrayXml + */ + public static final void writeIntArrayXml(int[] val, String name, + XmlSerializer out) + throws XmlPullParserException, java.io.IOException { + + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "int-array"); + if (name != null) { + out.attribute(null, "name", name); + } + + final int N = val.length; + out.attribute(null, "num", Integer.toString(N)); + + for (int i=0; iafter the tag that begins the map. + * + * @param parser The XmlPullParser from which to read the map data. + * @param endTag Name of the tag that will end the map, usually "map". + * @param name An array of one string, used to return the name attribute + * of the map's tag. + * + * @return HashMap The newly generated map. + * + * @see #readMapXml + */ + public static final HashMap readThisMapXml(XmlPullParser parser, String endTag, String[] name) + throws XmlPullParserException, java.io.IOException + { + HashMap map = new HashMap(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name); + if (name[0] != null) { + //System.out.println("Adding to map: " + name + " -> " + val); + map.put(name[0], val); + } else { + throw new XmlPullParserException( + "Map value without name attribute: " + parser.getName()); + } + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return map; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read an ArrayList object from an XmlPullParser. The XML data could + * previously have been generated by writeListXml(). The XmlPullParser + * must be positioned after the tag that begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "list". + * @param name An array of one string, used to return the name attribute + * of the list's tag. + * + * @return HashMap The newly generated list. + * + * @see #readListXml + */ + public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name) + throws XmlPullParserException, java.io.IOException + { + ArrayList list = new ArrayList(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name); + list.add(val); + //System.out.println("Adding to list: " + val); + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return list; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read a HashSet object from an XmlPullParser. The XML data could previously + * have been generated by writeSetXml(). The XmlPullParser must be positioned + * after the tag that begins the set. + * + * @param parser The XmlPullParser from which to read the set data. + * @param endTag Name of the tag that will end the set, usually "set". + * @param name An array of one string, used to return the name attribute + * of the set's tag. + * + * @return HashSet The newly generated set. + * + * @throws XmlPullParserException + * @throws java.io.IOException + * + * @see #readSetXml + */ + public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name) + throws XmlPullParserException, java.io.IOException { + HashSet set = new HashSet(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name); + set.add(val); + //System.out.println("Adding to set: " + val); + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return set; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read an int[] object from an XmlPullParser. The XML data could + * previously have been generated by writeIntArrayXml(). The XmlPullParser + * must be positioned after the tag that begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "list". + * @param name An array of one string, used to return the name attribute + * of the list's tag. + * + * @return Returns a newly generated int[]. + * + * @see #readListXml + */ + public static final int[] readThisIntArrayXml(XmlPullParser parser, + String endTag, String[] name) + throws XmlPullParserException, java.io.IOException { + + int num; + try { + num = Integer.parseInt(parser.getAttributeValue(null, "num")); + } catch (NullPointerException e) { + throw new XmlPullParserException( + "Need num attribute in byte-array"); + } catch (NumberFormatException e) { + throw new XmlPullParserException( + "Not a number in num attribute in byte-array"); + } + + int[] array = new int[num]; + int i = 0; + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + if (parser.getName().equals("item")) { + try { + array[i] = Integer.parseInt( + parser.getAttributeValue(null, "value")); + } catch (NullPointerException e) { + throw new XmlPullParserException( + "Need value attribute in item"); + } catch (NumberFormatException e) { + throw new XmlPullParserException( + "Not a number in value attribute in item"); + } + } else { + throw new XmlPullParserException( + "Expected item tag at: " + parser.getName()); + } + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return array; + } else if (parser.getName().equals("item")) { + i++; + } else { + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + + parser.getName()); + } + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Read a flattened object from an XmlPullParser. The XML data could + * previously have been written with writeMapXml(), writeListXml(), or + * writeValueXml(). The XmlPullParser must be positioned at the + * tag that defines the value. + * + * @param parser The XmlPullParser from which to read the object. + * @param name An array of one string, used to return the name attribute + * of the value's tag. + * + * @return Object The newly generated value object. + * + * @see #readMapXml + * @see #readListXml + * @see #writeValueXml + */ + public static final Object readValueXml(XmlPullParser parser, String[] name) + throws XmlPullParserException, java.io.IOException + { + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + return readThisValueXml(parser, name); + } else if (eventType == parser.END_TAG) { + throw new XmlPullParserException( + "Unexpected end tag at: " + parser.getName()); + } else if (eventType == parser.TEXT) { + throw new XmlPullParserException( + "Unexpected text: " + parser.getText()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Unexpected end of document"); + } + + private static final Object readThisValueXml(XmlPullParser parser, String[] name) + throws XmlPullParserException, java.io.IOException + { + final String valueName = parser.getAttributeValue(null, "name"); + final String tagName = parser.getName(); + + //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName); + + Object res; + + if (tagName.equals("null")) { + res = null; + } else if (tagName.equals("string")) { + String value = ""; + int eventType; + while ((eventType = parser.next()) != parser.END_DOCUMENT) { + if (eventType == parser.END_TAG) { + if (parser.getName().equals("string")) { + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + value); + return value; + } + throw new XmlPullParserException( + "Unexpected end tag in : " + parser.getName()); + } else if (eventType == parser.TEXT) { + value += parser.getText(); + } else if (eventType == parser.START_TAG) { + throw new XmlPullParserException( + "Unexpected start tag in : " + parser.getName()); + } + } + throw new XmlPullParserException( + "Unexpected end of document in "); + } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) { + // all work already done by readThisPrimitiveValueXml + } else if (tagName.equals("int-array")) { + parser.next(); + res = readThisIntArrayXml(parser, "int-array", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else if (tagName.equals("map")) { + parser.next(); + res = readThisMapXml(parser, "map", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else if (tagName.equals("list")) { + parser.next(); + res = readThisListXml(parser, "list", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else if (tagName.equals("set")) { + parser.next(); + res = readThisSetXml(parser, "set", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } else { + throw new XmlPullParserException( + "Unknown tag: " + tagName); + } + + // Skip through to end tag. + int eventType; + while ((eventType = parser.next()) != parser.END_DOCUMENT) { + if (eventType == parser.END_TAG) { + if (parser.getName().equals(tagName)) { + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; + } + throw new XmlPullParserException( + "Unexpected end tag in <" + tagName + ">: " + parser.getName()); + } else if (eventType == parser.TEXT) { + throw new XmlPullParserException( + "Unexpected text in <" + tagName + ">: " + parser.getName()); + } else if (eventType == parser.START_TAG) { + throw new XmlPullParserException( + "Unexpected start tag in <" + tagName + ">: " + parser.getName()); + } + } + throw new XmlPullParserException( + "Unexpected end of document in <" + tagName + ">"); + } + + private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName) + throws XmlPullParserException, java.io.IOException + { + try { + if (tagName.equals("int")) { + return Integer.parseInt(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("long")) { + return Long.valueOf(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("float")) { + return new Float(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("double")) { + return new Double(parser.getAttributeValue(null, "value")); + } else if (tagName.equals("boolean")) { + return Boolean.valueOf(parser.getAttributeValue(null, "value")); + } else { + return null; + } + } catch (NullPointerException e) { + throw new XmlPullParserException("Need value attribute in <" + tagName + ">"); + } catch (NumberFormatException e) { + throw new XmlPullParserException( + "Not a number in value attribute in <" + tagName + ">"); + } + } + + public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException + { + int type; + while ((type=parser.next()) != parser.START_TAG + && type != parser.END_DOCUMENT) { + ; + } + + if (type != parser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + if (!parser.getName().equals(firstElementName)) { + throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + + ", expected " + firstElementName); + } + } + + public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException + { + int type; + while ((type=parser.next()) != parser.START_TAG + && type != parser.END_DOCUMENT) { + ; + } + } + + public static boolean nextElementWithin(XmlPullParser parser, int outerDepth) + throws IOException, XmlPullParserException { + for (;;) { + int type = parser.next(); + if (type == XmlPullParser.END_DOCUMENT + || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) { + return false; + } + if (type == XmlPullParser.START_TAG + && parser.getDepth() == outerDepth + 1) { + return true; + } + } + } + + public static int readIntAttribute(XmlPullParser in, String name) throws IOException { + final String value = in.getAttributeValue(null, name); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new ProtocolException("problem parsing " + name + "=" + value + " as int"); + } + } + + public static void writeIntAttribute(XmlSerializer out, String name, int value) + throws IOException { + out.attribute(null, name, Integer.toString(value)); + } + + public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) { + final String value = in.getAttributeValue(null, name); + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public static long readLongAttribute(XmlPullParser in, String name) throws IOException { + final String value = in.getAttributeValue(null, name); + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + throw new ProtocolException("problem parsing " + name + "=" + value + " as long"); + } + } + + public static void writeLongAttribute(XmlSerializer out, String name, long value) + throws IOException { + out.attribute(null, name, Long.toString(value)); + } + + public static boolean readBooleanAttribute(XmlPullParser in, String name) { + final String value = in.getAttributeValue(null, name); + return Boolean.parseBoolean(value); + } + + public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value) + throws IOException { + out.attribute(null, name, Boolean.toString(value)); + } +} diff --git a/src/XPrivacy-master/XPrivacy-master/tools/addstring.sh b/src/XPrivacy-master/XPrivacy-master/tools/addstring.sh new file mode 100644 index 0000000..533694d --- /dev/null +++ b/src/XPrivacy-master/XPrivacy-master/tools/addstring.sh @@ -0,0 +1,9 @@ +#!/bin/bash +grep -RIl "\The privacy database was reset, because it was corrupt' + +#grep -RIl "\ + + + + + + + + + + + + diff --git a/src/XPrivacy-master/XPrivacy-master/tools/xxhdmi2.xcf b/src/XPrivacy-master/XPrivacy-master/tools/xxhdmi2.xcf new file mode 100644 index 0000000..571916b Binary files /dev/null and b/src/XPrivacy-master/XPrivacy-master/tools/xxhdmi2.xcf differ