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 授予广泛同意和特定同意:包含应用隐私政策的应用

要点:

  1. 首次加载(或应用更新)时,获得广泛同意,来传输将由应用处理的用户数据。
  2. 如果用户未授予广泛同意,请勿传输用户数据。
  3. 在传输需要特别细致的处理的用户数据之前获得特定同意。
  4. 如果用户未授予特定同意,请勿传输相应的数据。
  5. 向用户提供可以查看应用隐私策略的方法。
  6. 提供通过用户操作删除传输的数据的方法。
  7. 提供通过用户操作停止数据传输的方法。
  8. 使用 UUID 或 cookie 来跟踪用户数据。
  9. 将应用隐私策略的摘要版本放置在素材文件夹中。

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 授予广泛同意:包含应用隐私政策的应用

要点:

  1. 首次加载(或应用更新)时,获得广泛同意,来传输将由应用处理的用户数据。
  2. 如果用户未授予广泛同意,请勿传输用户数据。
  3. 向用户提供可以查看应用隐私策略的方法。
  4. 提供通过用户操作删除传输的数据的方法。
  5. 提供通过用户操作停止数据传输的方法。
  6. 使用 UUID 或 cookie 来跟踪用户数据。
  7. 将应用隐私策略的摘要版本放置在素材文件夹中。

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 不需要广泛同意:包含应用隐私策略的应用

要点:

  1. 向用户提供查看应用隐私策略的方法。
  2. 提供通过用户操作删除传输的数据的方法。
  3. 提供通过用户操作停止数据传输的方法
  4. 使用 UUID 或 cookie 来跟踪用户数据。
  5. 将应用隐私策略的摘要版本放置在素材文件夹中。

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 不包含应用隐私策略的应用

要点:

  1. 如果你的应用只使用它在设备中获取的信息,则不需要显示应用隐私策略。
  2. 在市场应用或类似应用的文档中,请注意应用不会将其获取的信息传输到外部。

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 处理隐私数据 示例代码相关推荐

  1. 安卓应用安全指南 5.5.3 处理隐私数据 高级话题

    5.5.3 处理隐私数据 高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA ...

  2. 安卓应用安全指南 5.5.2 处理隐私数据 规则书

    5.5.2 处理隐私数据 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4 ...

  3. 安卓应用安全指南 4.4.1 创建/使用服务 示例代码

    4.4.1 创建/使用服务 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA ...

  4. 安卓应用安全指南 4.1.1 创建/使用活动 示例代码

    4.1.1 创建/使用活动 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA ...

  5. android studio 实用指南,《Android Studio实用指南》4.27 使用演示模式(示例代码)

    本文节选自<Android Studio实用指南> 第4章第27节 作者: 毕小朋 目前本书已上传到百度阅读, 在百度中搜索[Anroid Studio实用指南]便可以找到本书. 什么是演 ...

  6. 安卓应用安全指南 4.4.3 创建/使用服务高级话题

    安卓应用安全指南 4.4.3 创建/使用服务高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC ...

  7. 安卓应用安全指南 4.6.3 处理文件 高级话题

    安卓应用安全指南 4.6.3 处理文件 高级话题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY ...

  8. 安卓应用安全指南 4.6.2 处理文件 规则书

    安卓应用安全指南 4.6.2 处理文件 规则书 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY- ...

  9. 安卓应用安全指南 4.6.1 处理文件 示例代码

    安卓应用安全指南 4.6.1 处理文件 示例代码 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY ...

最新文章

  1. 网络营销外包对于搜索引擎策略性调整网络营销外包专员如何解析
  2. windows 常用工具
  3. 汇编排序算法代码总结
  4. 社区O2O全面遇冷,社区金融如何避免走入独特陷阱
  5. 《梦断代码》随笔第2篇
  6. C# .net防止一个程序(WinForm)重复运行的方法。
  7. hal库开启中断关中断_【MCU】寄存器、标准库、HAL库、LL库,这么多库!你叫我怎么选?...
  8. 编写if语句时then子句为空语句_Python入门 5——循环语句及条件判断
  9. group by调优的一些测试
  10. h2测试软件,H2testw怎么测试 H2testw测试结果如何看的详细技巧
  11. [关系图谱] 二.Gephi导入共线矩阵构建作者关系图谱
  12. javaScript一元四次、三次方程求根算法
  13. 面试常见的逻辑推理题
  14. 三个和尚比身高,已告知三个和尚的身高
  15. HTTPS网站提示“此网站无法提供安全连接”
  16. win10可以上网但显示无网络连接
  17. 微信小程序 使用 uCharts 图表
  18. IP地址的三种表示方式是什么
  19. Hadoop fs 常用命令
  20. c语言交通违章编程代码,C语言程序设计之交通处罚单管理系统报告(内含代码)...

热门文章

  1. (85)FPGA显示激励(monitor)
  2. FPGA入门基础介绍
  3. qt与JAVA服务器通信_Qt实现的SSL通信客户端和服务器
  4. linux5怎么删除目录,Linux中目录的创建与删除命令使用说明
  5. Python文件的操作2
  6. 数据结构-二叉树、搜索树、平衡二叉树详解及C语言实现
  7. STM32 Flash详解
  8. libevent源码深度剖析五
  9. poll接口《来自Linux驱动程序开发实例》
  10. Android默认记住登录用户名,【教程】Android 记住密码和自动登录界面的实现