安卓应用安全指南 5.5.1 处理隐私数据 示例代码
5.5.1 处理隐私数据 示例代码
原书:Android Application Secure Design/Secure Coding Guidebook
译者:飞龙
协议:CC BY-NC-SA 4.0
在准备应用的隐私政策时,你可以使用“协助创建应用隐私政策的工具” [29]。 这些工具以 HTML 格式和 XML 格式输出两个文件 - 应用隐私策略的摘要版本和详细版本。 这些文件的 HTML 和 XML 内容符合 MIC SPI 的建议,包括搜索标签等特性。 在下面的示例代码中,我们将演示此工具的用法,并使用由这个工具产生的 HTML 文件来展示程序隐私策略。
[29] http://www.kddilabs.jp/tech/public-tech/appgen.html
更具体地说,你可以使用以下流程图来确定使用哪个示例代码。
这里,“广泛同意”一词,指代广泛许可,由用户在应用的首次加载时,通过展示和查看程序隐私策略授予应用,用于应用将用户数据传输到服务器。 相反,短语“特定同意”指代在传输特定用户数据之前,立即获得的预先同意。
5.5.1.1 授予广泛同意和特定同意:包含应用隐私政策的应用
要点:
- 首次加载(或应用更新)时,获得广泛同意,来传输将由应用处理的用户数据。
- 如果用户未授予广泛同意,请勿传输用户数据。
- 在传输需要特别细致的处理的用户数据之前获得特定同意。
- 如果用户未授予特定同意,请勿传输相应的数据。
- 向用户提供可以查看应用隐私策略的方法。
- 提供通过用户操作删除传输的数据的方法。
- 提供通过用户操作停止数据传输的方法。
- 使用 UUID 或 cookie 来跟踪用户数据。
- 将应用隐私策略的摘要版本放置在素材文件夹中。
MainActivity.java
package org.jssec.android.privacypolicy;
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import org.jssec.android.privacypolicy.ConfirmFragment.DialogListener;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends FragmentActivity implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, DialogListener {private static final String BASE_URL = "https://www.example.com/pp";private static final String GET_ID_URI = BASE_URL + "/get_id.php";private static final String SEND_DATA_URI = BASE_URL + "/send_data.php";private static final String DEL_ID_URI = BASE_URL + "/del_id.php";private static final String ID_KEY = "id";private static final String LOCATION_KEY = "location";private static final String NICK_NAME_KEY = "nickname";private static final String PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY = "privacyPolicyComprehensiveAgreed";private static final String PRIVACY_POLICY_DISCRETE_TYPE1_AGREED_KEY = "privacyPolicyDiscreteType1Agreed";private static final String PRIVACY_POLICY_PREF_NAME = "privacypolicy_preference";private static final int CONNECTION_FAILURE_RESOLUTION_REQUEST = 257;private String UserId = "";private LocationClient mLocationClient = null;private final int DIALOG_TYPE_COMPREHENSIVE_AGREEMENT = 1;private final int DIALOG_TYPE_PRE_CONFIRMATION = 2;private static final int VERSION_TO_SHOW_COMPREHENSIVE_AGREEMENT_ANEW = 1;private TextWatcher watchHandler = new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {boolean buttonEnable = (s.length() > 0);MainActivity.this.findViewById(R.id.buttonStart).setEnabled(buttonEnable);}@Overridepublic void afterTextChanged(Editable s) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// Fetch user ID from serverFetch user ID from servernew GetDataAsyncTask().execute();findViewById(R.id.buttonStart).setEnabled(false);((TextView) findViewById(R.id.editTextNickname)).addTextChangedListener(watchHandler);int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);if (resultCode == ConnectionResult.SUCCESS) {mLocationClient = new LocationClient(this, this, this);}}@Overrideprotected void onStart() {super.onStart();SharedPreferences pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);int privacyPolicyAgreed = pref.getInt(PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY, -1);if (privacyPolicyAgreed <= VERSION_TO_SHOW_COMPREHENSIVE_AGREEMENT_ANEW) {// *** POINT 1 *** On first launch (or application update), obtain broad consent to transmit user data that will be handled by the application.// When the application is updated, it is only necessary to renew the user's grant of broad consent if the updated application will handle new types of user data.ConfirmFragment dialog = ConfirmFragment.newInstance(R.string.privacyPolicy, R.string.agreePrivacyPolicy, DIALOG_TYPE_COMPREHENSIVE_AGREEMENT);dialog.setDialogListener(this);FragmentManager fragmentManager = getSupportFragmentManager();dialog.show(fragmentManager, "dialog");}// Used to obtain location dataif (mLocationClient != null) {mLocationClient.connect();}}@Overrideprotected void onStop() {if (mLocationClient != null) {mLocationClient.disconnect();}super.onStop();}public void onSendToServer(View view) {// Check the status of user consent.// Actually, it is necessary to obtain consent for each user data type.SharedPreferences pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);int privacyPolicyAgreed = pref.getInt(PRIVACY_POLICY_DISCRETE_TYPE1_AGREED_KEY, -1);if (privacyPolicyAgreed <= VERSION_TO_SHOW_COMPREHENSIVE_AGREEMENT_ANEW) {// *** POINT 3 *** Obtain specific consent before transmitting user data that requires particularly delicate handling.ConfirmFragment dialog = ConfirmFragment.newInstance(R.string.sendLocation, R.string.cofirmSendLocation, DIALOG_TYPE_PRE_CONFIRMATION);dialog.setDialogListener(this);FragmentManager fragmentManager = getSupportFragmentManager();dialog.show(fragmentManager, "dialog");} else {// Start transmission, since it has the user consent.onPositiveButtonClick(DIALOG_TYPE_PRE_CONFIRMATION);}}public void onPositiveButtonClick(int type) {if (type == DIALOG_TYPE_COMPREHENSIVE_AGREEMENT) {// *** POINT 1 *** On first launch (or application update), obtain broad consent to transmit user data that will be handled by the application.SharedPreferences.Editor pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE).edit();pref.putInt(PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY, getVersionCode());pref.apply();} else if (type == DIALOG_TYPE_PRE_CONFIRMATION) {// *** POINT 3 *** Obtain specific consent before transmitting user data that requires particularly delicate handling.if (mLocationClient != null && mLocationClient.isConnected()) {Location currentLocation = mLocationClient.getLastLocation();if (currentLocation != null) {String locationData = "Latitude:" + currentLocation.getLatitude() + ", Longitude:" +currentLocation.getLongitude();String nickname = ((TextView) findViewById(R.id.editTextNickname)).getText().toString();Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + "¥n - nickname :" + nickname + "¥n - location : " + locationData, Toast.LENGTH_SHORT).show();new SendDataAsyncTack().execute(SEND_DATA_URI, UserId, locationData, nickname);}}// Store the status of user consent.// Actually, it is necessary to obtain consent for each user data type.SharedPreferences.Editor pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE).edit();pref.putInt(PRIVACY_POLICY_DISCRETE_TYPE1_AGREED_KEY, getVersionCode());pref.apply();}}public void onNegativeButtonClick(int type) {if (type == DIALOG_TYPE_COMPREHENSIVE_AGREEMENT) {// *** POINT 2 *** If the user does not grant general consent, do not transmit user data.// In this sample application we terminate the application in this case.finish();} else if (type == DIALOG_TYPE_PRE_CONFIRMATION) {// *** POINT 4 *** If the user does not grant specific consent, do not transmit the corresponding data.// The user did not grant consent, so we do nothing.}}private int getVersionCode() {int versionCode = -1;PackageManager packageManager = this.getPackageManager();try {PackageInfo packageInfo = packageManager.getPackageInfo(this.getPackageName(), PackageManager.GET_ACTIVITIES);versionCode = packageInfo.versionCode;} catch (NameNotFoundException e) {// This is sample, so omit the exception process}return versionCode;}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.action_show_pp:// *** POINT 5 *** Provide methods by which the user can review the application privacy policy.Intent intent = new Intent();intent.setClass(this, WebViewAssetsActivity.class);startActivity(intent);return true;case R.id.action_del_id:// *** POINT 6 *** Provide methods by which transmitted data can be deleted by user operations.new SendDataAsyncTack().execute(DEL_ID_URI, UserId);return true;case R.id.action_donot_send_id:// *** POINT 7 *** Provide methods by which transmitting data can be stopped by user operations.// If the user stop sending data, user consent is deemed to have been revoked.SharedPreferences.Editor pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE).edit();pref.putInt(PRIVACY_POLICY_COMPREHENSIVE_AGREED_KEY, 0);pref.apply();// In this sample application if the user data cannot be sent by user operations,// finish the application because we do nothing.String message = getString(R.string.stopSendUserData);Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + message, Toast.LENGTH_SHORT).show();finish();return true;}return false;}@Overridepublic void onConnected(Bundle connectionHint) {if (mLocationClient != null && mLocationClient.isConnected()) {Location currentLocation = mLocationClient.getLastLocation();if (currentLocation != null) {String locationData = "Latitude ¥t: " + currentLocation.getLatitude() + "¥n¥tLongitude ¥t: " + currentLocation.getLongitude();String text = "¥n" + getString(R.string.your_location_title) + "¥n¥t" + locationData;TextView appText = (TextView) findViewById(R.id.appText);appText.setText(text);}}}@Overridepublic void onConnectionFailed(ConnectionResult result) {if (result.hasResolution()) {try {result.startResolutionForResult(this, CONNECTION_FAILURE_RESOLUTION_REQUEST);} catch (IntentSender.SendIntentException e) {e.printStackTrace();}}}@Overridepublic void onDisconnected() {mLocationClient = null;}private class GetDataAsyncTask extends AsyncTask<String, Void, String> {private String extMessage = "";@Overrideprotected String doInBackground(String... params) {// *** POINT 8 *** Use UUIDs or cookies to keep track of user data// In this sample we use an ID generated on the server sideSharedPreferences sp = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);UserId = sp.getString(ID_KEY, null);if (UserId == null) {// No token in SharedPreferences; fetch ID from servertry {UserId = NetworkUtil.getCookie(GET_ID_URI, "", "id");} catch (IOException e) {// Catch exceptions such as certification errorsextMessage = e.toString();}// Store the fetched ID in SharedPreferencessp.edit().putString(ID_KEY, UserId).commit();}return UserId;}@Overrideprotected void onPostExecute(final String data) {String status = (data != null) ? "success" : "error";Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + status + " : " +extMessage, Toast.LENGTH_SHORT).show();}}private class SendDataAsyncTack extends AsyncTask<String, Void, Boolean> {private String extMessage = "";@Overrideprotected Boolean doInBackground(String... params) {String url = params[0];String id = params[1];String location = params.length > 2 ? params[2] : null;String nickname = params.length > 3 ? params[3] : null;Boolean result = false;try {JSONObject jsonData = new JSONObject();jsonData.put(ID_KEY, id);if (location != null)jsonData.put(LOCATION_KEY, location);if (nickname != null)jsonData.put(NICK_NAME_KEY, nickname);NetworkUtil.sendJSON(url, "", jsonData.toString());result = true;} catch (IOException e) {// Catch exceptions such as certification errorsextMessage = e.toString();} catch (JSONException e) {extMessage = e.toString();}return result;}@Overrideprotected void onPostExecute(Boolean result) {String status = result ? "Success" : "Error";Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + status + " : " +extMessage, Toast.LENGTH_SHORT).show();}}
}
ConfirmFragment.java
package org.jssec.android.privacypolicy;import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;public class ConfirmFragment extends DialogFragment {private DialogListener mListener = null;public static interface DialogListener {public void onPositiveButtonClick(int type);public void onNegativeButtonClick(int type);}public static ConfirmFragment newInstance(int title, int sentence, int type) {ConfirmFragment fragment = new ConfirmFragment();Bundle args = new Bundle();args.putInt("title", title);args.putInt("sentence", sentence);args.putInt("type", type);fragment.setArguments(args);return fragment;}@Overridepublic Dialog onCreateDialog(Bundle args) {// *** POINT 1 *** On first launch (or application update), obtain broad consent to transmit user data that will be handled by the application.// *** POINT 3 *** Obtain specific consent before transmitting user data that requires particularly delicate handling.final int title = getArguments().getInt("title");final int sentence = getArguments().getInt("sentence");final int type = getArguments().getInt("type");LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);View content = inflater.inflate(R.layout.fragment_comfirm, null);TextView linkPP = (TextView) content.findViewById(R.id.tx_link_pp);linkPP.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// *** POINT 5 *** Provide methods by which the user can review the application privacy policy.Intent intent = new Intent();intent.setClass(getActivity(), WebViewAssetsActivity.class);startActivity(intent);}});AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());builder.setIcon(R.drawable.ic_launcher);builder.setTitle(title);builder.setMessage(sentence);builder.setView(content);builder.setPositiveButton(R.string.buttonConsent, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int whichButton) {if (mListener != null) {mListener.onPositiveButtonClick(type);}}});builder.setNegativeButton(R.string.buttonDonotConsent, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int whichButton) {if (mListener != null) {mListener.onNegativeButtonClick(type);}}});Dialog dialog = builder.create();dialog.setCanceledOnTouchOutside(false);return dialog;}@Overridepublic void onAttach(Activity activity) {super.onAttach(activity);if (!(activity instanceof DialogListener)) {throw new ClassCastException(activity.toString() + " must implement DialogListener.");}mListener = (DialogListener) activity;}public void setDialogListener(DialogListener listener) {mListener = listener;}
}
WebViewAssetsActivity.java
package org.jssec.android.privacypolicy;import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;public class WebViewAssetsActivity extends Activity {// *** POINT 9 *** Place a summary version of the application privacy policy in the assets folderprivate static final String ABST_PP_URL = "file:///android_asset/PrivacyPolicy/app-policy-abst-privacypolicy-1.0.html";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);WebView webView = (WebView) findViewById(R.id.webView);WebSettings webSettings = webView.getSettings();webSettings.setAllowFileAccess(false);webView.loadUrl(ABST_PP_URL);}
}
5.5.1.2 授予广泛同意:包含应用隐私政策的应用
要点:
- 首次加载(或应用更新)时,获得广泛同意,来传输将由应用处理的用户数据。
- 如果用户未授予广泛同意,请勿传输用户数据。
- 向用户提供可以查看应用隐私策略的方法。
- 提供通过用户操作删除传输的数据的方法。
- 提供通过用户操作停止数据传输的方法。
- 使用 UUID 或 cookie 来跟踪用户数据。
- 将应用隐私策略的摘要版本放置在素材文件夹中。
MainActivity.java
package org.jssec.android.privacypolicynopreconfirm;import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import org.jssec.android.privacypolicynopreconfirm.MainActivity;
import org.jssec.android.privacypolicynopreconfirm.R;
import org.jssec.android.privacypolicynopreconfirm.ConfirmFragment.DialogListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.telephony.TelephonyManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends FragmentActivity implements DialogListener {private final String BASE_URL = "https://www.example.com/pp";private final String GET_ID_URI = BASE_URL + "/get_id.php";private final String SEND_DATA_URI = BASE_URL + "/send_data.php";private final String DEL_ID_URI = BASE_URL + "/del_id.php";private final String ID_KEY = "id";private final String NICK_NAME_KEY = "nickname";private final String IMEI_KEY = "imei";private final String PRIVACY_POLICY_AGREED_KEY = "privacyPolicyAgreed";private final String PRIVACY_POLICY_PREF_NAME = "privacypolicy_preference";private String UserId = "";private final int DIALOG_TYPE_COMPREHENSIVE_AGREEMENT = 1;private final int VERSION_TO_SHOW_COMPREHENSIVE_AGREEMENT_ANEW = 1;private TextWatcher watchHandler = new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {boolean buttonEnable = (s.length() > 0);MainActivity.this.findViewById(R.id.buttonStart).setEnabled(buttonEnable);}@Overridepublic void afterTextChanged(Editable s) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// Fetch user ID from serverFetch user ID from servernew GetDataAsyncTask().execute();findViewById(R.id.buttonStart).setEnabled(false);((TextView) findViewById(R.id.editTextNickname)).addTextChangedListener(watchHandler);}@Overrideprotected void onStart() {super.onStart();SharedPreferences pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);int privacyPolicyAgreed = pref.getInt(PRIVACY_POLICY_AGREED_KEY, -1);if (privacyPolicyAgreed <= VERSION_TO_SHOW_COMPREHENSIVE_AGREEMENT_ANEW) {// *** POINT 1 *** On first launch (or application update), obtain broad consent to transmit user data that will be handled by the application.// When the application is updated, it is only necessary to renew the user's grant of broad consent if the updated application will handle new types of user data.ConfirmFragment dialog = ConfirmFragment.newInstance(R.string.privacyPolicy, R.string.agreePrivacyPolicy, DIALOG_TYPE_COMPREHENSIVE_AGREEMENT);dialog.setDialogListener(this);FragmentManager fragmentManager = getSupportFragmentManager();dialog.show(fragmentManager, "dialog");}}public void onSendToServer(View view) {String nickname = ((TextView) findViewById(R.id.editTextNickname)).getText().toString();TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);String imei = tm.getDeviceId();Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + "¥n - nickname : " + nickname + ", imei = " + imei, Toast.LENGTH_SHORT).show();new SendDataAsyncTack().execute(SEND_DATA_URI, UserId, nickname, imei);}public void onPositiveButtonClick(int type) {if (type == DIALOG_TYPE_COMPREHENSIVE_AGREEMENT) {// *** POINT 1 *** On first launch (or application update), obtain broad consent to transmituser data that will be handled by the application.SharedPreferences.Editor pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE).edit();pref.putInt(PRIVACY_POLICY_AGREED_KEY, getVersionCode());pref.apply();}}public void onNegativeButtonClick(int type) {if (type == DIALOG_TYPE_COMPREHENSIVE_AGREEMENT) {// *** POINT 2 *** If the user does not grant general consent, do not transmit user data.// In this sample application we terminate the application in this case.finish();}}private int getVersionCode() {int versionCode = -1;PackageManager packageManager = this.getPackageManager();try {PackageInfo packageInfo = packageManager.getPackageInfo(this.getPackageName(), PackageManager.GET_ACTIVITIES);versionCode = packageInfo.versionCode;} catch (NameNotFoundException e) {// This is sample, so omit the exception process}return versionCode;}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.action_show_pp:// *** POINT 3 *** Provide methods by which the user can review the application privacy policy.Intent intent = new Intent();intent.setClass(this, WebViewAssetsActivity.class);startActivity(intent);return true;case R.id.action_del_id:// *** POINT 4 *** Provide methods by which transmitted data can be deleted by user operations.new SendDataAsyncTack().execute(DEL_ID_URI, UserId);return true;case R.id.action_donot_send_id:// *** POINT 5 *** Provide methods by which transmitting data can be stopped by user operations.// If the user stop sending data, user consent is deemed to have been revoked.SharedPreferences.Editor pref = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE).edit();pref.putInt(PRIVACY_POLICY_AGREED_KEY, 0);pref.apply();// In this sample application if the user data cannot be sent by user operations,// finish the application because we do nothing.String message = getString(R.string.stopSendUserData);Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + message, Toast.LENGTH_SHORT).show();finish();return true; }return false;}private class GetDataAsyncTask extends AsyncTask<String, Void, String> {private String extMessage = "";@Overrideprotected String doInBackground(String... params) {// *** POINT 6 *** Use UUIDs or cookies to keep track of user data// In this sample we use an ID generated on the server sideSharedPreferences sp = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);UserId = sp.getString(ID_KEY, null);if (UserId == null) {// No token in SharedPreferences; fetch ID from servertry {UserId = NetworkUtil.getCookie(GET_ID_URI, "", "id");} catch (IOException e) {// Catch exceptions such as certification errorsextMessage = e.toString();}// Store the fetched ID in SharedPreferencessp.edit().putString(ID_KEY, UserId).commit();}return UserId;}@Overrideprotected void onPostExecute(final String data) {String status = (data != null) ? "success" : "error";Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + status + " : " +extMessage, Toast.LENGTH_SHORT).show();}}private class SendDataAsyncTack extends AsyncTask<String, Void, Boolean> {private String extMessage = "";@Overrideprotected Boolean doInBackground(String... params) {String url = params[0];String id = params[1];String nickname = params.length > 2 ? params[2] : null;String imei = params.length > 3 ? params[3] : null;Boolean result = false;try {JSONObject jsonData = new JSONObject();jsonData.put(ID_KEY, id);if (nickname != null)jsonData.put(NICK_NAME_KEY, nickname);if (imei != null)jsonData.put(IMEI_KEY, imei);NetworkUtil.sendJSON(url, "", jsonData.toString());result = true;} catch (IOException e) {// Catch exceptions such as certification errorsextMessage = e.toString();} catch (JSONException e) {extMessage = e.toString();}return result;}@Overrideprotected void onPostExecute(Boolean result) {String status = result ? "Success" : "Error";Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + status + " : " +extMessage, Toast.LENGTH_SHORT).show();}}
}
ConfirmFragment.java
package org.jssec.android.privacypolicynopreconfirm;import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;public class ConfirmFragment extends DialogFragment {private DialogListener mListener = null;public static interface DialogListener {public void onPositiveButtonClick(int type);public void onNegativeButtonClick(int type);}public static ConfirmFragment newInstance(int title, int sentence, int type) {ConfirmFragment fragment = new ConfirmFragment();Bundle args = new Bundle();args.putInt("title", title);args.putInt("sentence", sentence);args.putInt("type", type);fragment.setArguments(args);return fragment;}@Overridepublic Dialog onCreateDialog(Bundle args) {// *** POINT 1 *** On first launch (or application update), obtain broad consent to transmit user data that will be handled by the application.final int title = getArguments().getInt("title");final int sentence = getArguments().getInt("sentence");final int type = getArguments().getInt("type");LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);View content = inflater.inflate(R.layout.fragment_comfirm, null);TextView linkPP = (TextView) content.findViewById(R.id.tx_link_pp);linkPP.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// *** POINT 3 *** Provide methods by which the user can review the application privacy policy.Intent intent = new Intent();intent.setClass(getActivity(), WebViewAssetsActivity.class);startActivity(intent);}});AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());builder.setIcon(R.drawable.ic_launcher);builder.setTitle(title);builder.setMessage(sentence);builder.setView(content);builder.setPositiveButton(R.string.buttonConsent, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int whichButton) {if (mListener != null) {mListener.onPositiveButtonClick(type);}}});builder.setNegativeButton(R.string.buttonDonotConsent, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int whichButton) {if (mListener != null) {mListener.onNegativeButtonClick(type);}}});Dialog dialog = builder.create();dialog.setCanceledOnTouchOutside(false);return dialog;}@Overridepublic void onAttach(Activity activity) {super.onAttach(activity);if (!(activity instanceof DialogListener)) {throw new ClassCastException(activity.toString() + " must implement DialogListener.");}mListener = (DialogListener) activity;}public void setDialogListener(DialogListener listener) {mListener = listener;}
}
WebViewAssetsActivity.java
package org.jssec.android.privacypolicynopreconfirm;import org.jssec.android.privacypolicynopreconfirm.R;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;public class WebViewAssetsActivity extends Activity {// *** POINT 7 *** Place a summary version of the application privacy policy in the assets folderprivate final String ABST_PP_URL = "file:///android_asset/PrivacyPolicy/app-policy-abst-privacypolicy-1.0.html";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);WebView webView = (WebView) findViewById(R.id.webView);WebSettings webSettings = webView.getSettings();webSettings.setAllowFileAccess(false);webView.loadUrl(ABST_PP_URL);}
}
5.5.1.3 不需要广泛同意:包含应用隐私策略的应用
要点:
- 向用户提供查看应用隐私策略的方法。
- 提供通过用户操作删除传输的数据的方法。
- 提供通过用户操作停止数据传输的方法
- 使用 UUID 或 cookie 来跟踪用户数据。
- 将应用隐私策略的摘要版本放置在素材文件夹中。
MainActivity.java
package org.jssec.android.privacypolicynocomprehensive;import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends FragmentActivity {private static final String BASE_URL = "https://www.example.com/pp";private static final String GET_ID_URI = BASE_URL + "/get_id.php";private static final String SEND_DATA_URI = BASE_URL + "/send_data.php";private static final String DEL_ID_URI = BASE_URL + "/del_id.php";private static final String ID_KEY = "id";private static final String NICK_NAME_KEY = "nickname";private static final String PRIVACY_POLICY_PREF_NAME = "privacypolicy_preference";private String UserId = "";private TextWatcher watchHandler = new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {boolean buttonEnable = (s.length() > 0);MainActivity.this.findViewById(R.id.buttonStart).setEnabled(buttonEnable);}@Overridepublic void afterTextChanged(Editable s) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// Fetch user ID from serverFetch user ID from servernew GetDataAsyncTask().execute();findViewById(R.id.buttonStart).setEnabled(false);((TextView) findViewById(R.id.editTextNickname)).addTextChangedListener(watchHandler);}public void onSendToServer(View view) {String nickname = ((TextView) findViewById(R.id.editTextNickname)).getText().toString();Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + "¥n - nickname : " + nickname, Toast.LENGTH_SHORT).show();new sendDataAsyncTack().execute(SEND_DATA_URI, UserId, nickname);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.action_show_pp:// *** POINT 1 *** Provide methods by which the user can review the application privacy policy.Intent intent = new Intent();intent.setClass(this, WebViewAssetsActivity.class);startActivity(intent);return true;case R.id.action_del_id:// *** POINT 2 *** Provide methods by which transmitted data can be deleted by user operations.new sendDataAsyncTack().execute(DEL_ID_URI, UserId);return true;case R.id.action_donot_send_id:// *** POINT 3 *** Provide methods by which transmitting data can be stopped by user operations.// In this sample application if the user data cannot be sent by user operations,// finish the application because we do nothing.String message = getString(R.string.stopSendUserData);Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + message, Toast.LENGTH_SHORT).show();finish();return true;}return false;}private class GetDataAsyncTask extends AsyncTask<String, Void, String> {private String extMessage = "";@Overrideprotected String doInBackground(String... params) {// *** POINT 4 *** Use UUIDs or cookies to keep track of user data// In this sample we use an ID generated on the server sideSharedPreferences sp = getSharedPreferences(PRIVACY_POLICY_PREF_NAME, MODE_PRIVATE);UserId = sp.getString(ID_KEY, null);if (UserId == null) {// No token in SharedPreferences; fetch ID from servertry {UserId = NetworkUtil.getCookie(GET_ID_URI, "", "id");} catch (IOException e) {// Catch exceptions such as certification errorsextMessage = e.toString();}// Store the fetched ID in SharedPreferencessp.edit().putString(ID_KEY, UserId).commit();}return UserId;}@Overrideprotected void onPostExecute(final String data) {String status = (data != null) ? "success" : "error";Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + status + " : " +extMessage, Toast.LENGTH_SHORT).show();}}private class sendDataAsyncTack extends AsyncTask<String, Void, Boolean> {private String extMessage = "";@Overrideprotected Boolean doInBackground(String... params) {String url = params[0];String id = params[1];String nickname = params.length > 2 ? params[2] : null;Boolean result = false;try {JSONObject jsonData = new JSONObject();jsonData.put(ID_KEY, id);if (nickname != null)jsonData.put(NICK_NAME_KEY, nickname);NetworkUtil.sendJSON(url, "", jsonData.toString());result = true;} catch (IOException e) {// Catch exceptions such as certification errorsextMessage = e.toString();} catch (JSONException e) {extMessage = e.toString();}return result;}@Overrideprotected void onPostExecute(Boolean result) {String status = result ? "Success" : "Error";Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + " - " + status + " : " +extMessage, Toast.LENGTH_SHORT).show();}}
}
WebViewAssetsActivity.java
package org.jssec.android.privacypolicynocomprehensive;import org.jssec.android.privacypolicynocomprehensive.R;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;public class WebViewAssetsActivity extends Activity {// *** POINT 5 *** Place a summary version of the application privacy policy in the assets folderprivate static final String ABST_PP_URL = "file:///android_asset/PrivacyPolicy/app-policy-abst-privacypolicy-1.0.html";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);WebView webView = (WebView) findViewById(R.id.webView);WebSettings webSettings = webView.getSettings();webSettings.setAllowFileAccess(false);webView.loadUrl(ABST_PP_URL);}
}
5.5.1.4 不包含应用隐私策略的应用
要点:
- 如果你的应用只使用它在设备中获取的信息,则不需要显示应用隐私策略。
- 在市场应用或类似应用的文档中,请注意应用不会将其获取的信息传输到外部。
MainActivity.java
package org.jssec.android.privacypolicynoinfosent;import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.content.Intent;
import android.content.IntentSender;
import android.support.v4.app.FragmentActivity;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends FragmentActivity implements GooglePlayServicesClient.ConnectionCallbacks,GooglePlayServicesClient.OnConnectionFailedListener {private LocationClient mLocationClient = null;private final int CONNECTION_FAILURE_RESOLUTION_REQUEST = 257;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mLocationClient = new LocationClient(this, this, this);}@Overrideprotected void onStart() {super.onStart();// Used to obtain location dataif (mLocationClient != null) {mLocationClient.connect();}}@Overrideprotected void onStop() {if (mLocationClient != null) {mLocationClient.disconnect();}super.onStop();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}public void onStartMap(View view) {// *** POINT 1 *** You do not need to display an application privacy policy if your application will only use the information it obtains within the device.if (mLocationClient != null && mLocationClient.isConnected()) {Location currentLocation = mLocationClient.getLastLocation();if (currentLocation != null) {Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("geo:" + currentLocation.getLatitude() + "," + currentLocation.getLongitude()));startActivity(intent);}}}@Overridepublic void onConnected(Bundle connectionHint) {if (mLocationClient != null && mLocationClient.isConnected()) {Location currentLocation = mLocationClient.getLastLocation();if (currentLocation != null) {String locationData = "Latitude ¥t: " + currentLocation.getLatitude() + "¥n¥tLongitude ¥t: " + currentLocation.getLongitude();String text = "¥n" + getString(R.string.your_location_title) + "¥n¥t" + locationData;Toast.makeText(MainActivity.this, this.getClass().getSimpleName() + text, Toast.LENGTH_SHORT).show();TextView appText = (TextView) findViewById(R.id.appText);appText.setText(text);}}}@Overridepublic void onConnectionFailed(ConnectionResult result) {if (result.hasResolution()) {try {result.startResolutionForResult(this, CONNECTION_FAILURE_RESOLUTION_REQUEST);} catch (IntentSender.SendIntentException e) {e.printStackTrace();}}}@Overridepublic void onDisconnected() {mLocationClient = null;Toast.makeText(this, "Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();}
}
市场上的示例如下。
安卓应用安全指南 5.5.1 处理隐私数据 示例代码相关推荐
- 安卓应用安全指南 5.5.3 处理隐私数据 高级话题
5.5.3 处理隐私数据 高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA ...
- 安卓应用安全指南 5.5.2 处理隐私数据 规则书
5.5.2 处理隐私数据 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4 ...
- 安卓应用安全指南 4.4.1 创建/使用服务 示例代码
4.4.1 创建/使用服务 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA ...
- 安卓应用安全指南 4.1.1 创建/使用活动 示例代码
4.1.1 创建/使用活动 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA ...
- android studio 实用指南,《Android Studio实用指南》4.27 使用演示模式(示例代码)
本文节选自<Android Studio实用指南> 第4章第27节 作者: 毕小朋 目前本书已上传到百度阅读, 在百度中搜索[Anroid Studio实用指南]便可以找到本书. 什么是演 ...
- 安卓应用安全指南 4.4.3 创建/使用服务高级话题
安卓应用安全指南 4.4.3 创建/使用服务高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC ...
- 安卓应用安全指南 4.6.3 处理文件 高级话题
安卓应用安全指南 4.6.3 处理文件 高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY ...
- 安卓应用安全指南 4.6.2 处理文件 规则书
安卓应用安全指南 4.6.2 处理文件 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY- ...
- 安卓应用安全指南 4.6.1 处理文件 示例代码
安卓应用安全指南 4.6.1 处理文件 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY ...
最新文章
- 网络营销外包对于搜索引擎策略性调整网络营销外包专员如何解析
- windows 常用工具
- 汇编排序算法代码总结
- 社区O2O全面遇冷,社区金融如何避免走入独特陷阱
- 《梦断代码》随笔第2篇
- C# .net防止一个程序(WinForm)重复运行的方法。
- hal库开启中断关中断_【MCU】寄存器、标准库、HAL库、LL库,这么多库!你叫我怎么选?...
- 编写if语句时then子句为空语句_Python入门 5——循环语句及条件判断
- group by调优的一些测试
- h2测试软件,H2testw怎么测试 H2testw测试结果如何看的详细技巧
- [关系图谱] 二.Gephi导入共线矩阵构建作者关系图谱
- javaScript一元四次、三次方程求根算法
- 面试常见的逻辑推理题
- 三个和尚比身高,已告知三个和尚的身高
- HTTPS网站提示“此网站无法提供安全连接”
- win10可以上网但显示无网络连接
- 微信小程序 使用 uCharts 图表
- IP地址的三种表示方式是什么
- Hadoop fs 常用命令
- c语言交通违章编程代码,C语言程序设计之交通处罚单管理系统报告(内含代码)...