首页 文章 精选 留言 我的

精选列表

搜索[安卓设备管理],共8535篇文章
优秀的个人博客,低调大师

安卓运动圆环自定义View

运动圆环自定义View.gif 记得这个东西原来有个同事问过我,当时正在自学自定义View和属性动画这一块,对这个功能也没有写过,所以就google了一下,发了几个类似效果的github项目给朋友,今天礼拜天难得有心情写写代码,所以想想实现一下这个自定义View的效果。 首先,我们从这个gif的效果图中就可以得知这个自定义View我们需要哪些自定义属性,内部圆环的颜色、外部圆环的颜色、圆环的宽度、字体的大小、颜色,话不多说,直接撸码。 <!-- 运动圆环自定义属性 --> <declare-styleable name="MotionCrcle"> <attr name="outerCrcleColor" format="color"></attr> <attr name="innerCrcleColor" format="color"></attr> <attr name="crcleTextColor" format="color"></attr> <attr name="crcleTextSize" format="integer"></attr> <attr name="crcleWidth" format="integer"></attr> </declare-styleable> /** * Created by Administrator on 2018/5/13. * 运动圆环自定义View */ public class MotionCrcle extends View { /** * 外部圆环颜色 */ private int mOuterColor = Color.BLUE; /** * 里面圆环颜色 */ private int mInnerColor = Color.RED; /** * 跑步数的文字大小 */ private int mTextSize = 30; /** * 跑步文字的颜色 */ private int mTextColor = Color.BLACK; /** * 圆环的宽度 */ private int mCrcleWidth = 45; private Paint mOuterPaint, mInnerPaint, mTextPaint; private float mMaxSteps = 0; private int mCurrentSteps = 0; public MotionCrcle(Context context) { this(context, null); } public MotionCrcle(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MotionCrcle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MotionCrcle); mOuterColor = array.getColor(R.styleable.MotionCrcle_outerCrcleColor, mOuterColor); mInnerColor = array.getColor(R.styleable.MotionCrcle_innerCrcleColor, mInnerColor); mCrcleWidth = array.getInt(R.styleable.MotionCrcle_crcleWidth, mCrcleWidth); mTextColor = array.getColor(R.styleable.MotionCrcle_crcleTextColor, mTextColor); mTextSize = array.getInt(R.styleable.MotionCrcle_crcleTextSize, mTextSize); initPaint(); array.recycle(); } public synchronized void setmMaxSteps(float mMaxSteps) { this.mMaxSteps = mMaxSteps; } public synchronized void setmCurrentSteps(int mCurrentSteps) { this.mCurrentSteps = mCurrentSteps; invalidate(); } private void initPaint() { mOuterPaint = new Paint(); mOuterPaint.setAntiAlias(true); mOuterPaint.setStrokeWidth(mCrcleWidth); mOuterPaint.setStrokeCap(Paint.Cap.ROUND); mOuterPaint.setStyle(Paint.Style.STROKE); mOuterPaint.setColor(mOuterColor); mInnerPaint = new Paint(); mInnerPaint.setAntiAlias(true); mInnerPaint.setStrokeWidth(mCrcleWidth); mInnerPaint.setStrokeCap(Paint.Cap.ROUND); mInnerPaint.setStyle(Paint.Style.STROKE); mInnerPaint.setColor(mInnerColor); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(mTextSize); mTextPaint.setColor(mTextColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if(width != height){ /** * 因为运动圆环是一个正方形,所以我们要设置一个最小值作为View的长度 */ int min = Math.min(width, height); widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, MeasureSpec.EXACTLY); heightMeasureSpec = MeasureSpec.makeMeasureSpec(min, MeasureSpec.EXACTLY); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onDraw(Canvas canvas) { //因为圆环本身是具有宽度的,所以我们这里左边的顶点要往右mCrcleWidth个距离,右边底部的点要往左,所以要减去mCrcleWidth RectF recf = new RectF(mCrcleWidth, mCrcleWidth, getWidth() - mCrcleWidth, getHeight() - mCrcleWidth); //135是外部圆环的初始角度,270是所画的总角度数 canvas.drawArc(recf, 135, 270, false, mOuterPaint); if(mCurrentSteps == 0){ return; } //算出当前步数是最大步数的比值 float angle = mCurrentSteps / mMaxSteps; canvas.drawArc(recf, 135, 270 * angle, false, mInnerPaint); String stepText = mCurrentSteps+""; Rect bounds = new Rect(); //算出步数文字的baseLine,也就是基准线 mTextPaint.getTextBounds(stepText, 0, stepText.length(), bounds); int x = getWidth() / 2 - bounds.width() / 2; Paint.FontMetricsInt metrices = mTextPaint.getFontMetricsInt(); int diffY = (metrices.bottom - metrices.top) / 2 - metrices.bottom; int baseLine = getHeight() / 2 + diffY; canvas.drawText(stepText, x, baseLine, mTextPaint); } } 文字基准线说明图.jpg 文字的绘制和画圆画弧不一样,其实仔细想想也明白,如果绘制按照左上角开始的话是不现实的,因为文字不可能是简单的顶部或底部对其,应该是重心对齐,简单说就是基准线,所以代码中基准线的算法是bottom-top再除以2减去bottom,以基准线开始绘制,top就是负数,bottom是正数。 public class MainActivity extends AppCompatActivity { private Button mBtn; private baohuo.rzj.com.myapplication.Views.MotionCrcle mCrcle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtn = (Button) findViewById(R.id.btn); mCrcle = (MotionCrcle) findViewById(R.id.motion_crcle); mCrcle.setmMaxSteps(10000); final ValueAnimator animator = ObjectAnimator.ofFloat(0,4500); animator.setDuration(2000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float value = (float) valueAnimator.getAnimatedValue(); mCrcle.setmCurrentSteps((int) value); } }); mBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { animator.start(); } }); } } 以上是Activity的代码,非常简单,不做解释,这个值得一提的是,我在写的时候内部圆环一直没有绘制,文字在不断变化,通过debug发现,我把步数最大值和当前步数设置为int,一个小的int除以大的int的到的只有0,所以我把最大步数改为float,这样得出的值就有小数位,最后附上xml代码。 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="baohuo.rzj.com.myapplication.MainActivity"> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="30dp" android:text="运动圆环开启" android:gravity="center"/> <baohuo.rzj.com.myapplication.Views.MotionCrcle android:id="@+id/motion_crcle" android:layout_below="@+id/btn" android:layout_width="200dp" android:layout_height="200dp" app:outerCrcleColor="@color/colorPrimary" app:innerCrcleColor="@color/colorAccent" app:crcleTextColor="@color/colorPrimaryDark" app:crcleTextSize="40" app:crcleWidth="20"/> </RelativeLayout>

优秀的个人博客,低调大师

安卓应用安全指南 六、困难问题

六、困难问题 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 在 Android 中,由于 Android 操作系统规范或 Android 操作系统提供的功能,难以确保应用实现的安全性。 这些功能被恶意第三方滥用或用户不小心使用,始终存在可能导致信息泄露等安全问题的风险。 本章通过指出开发人员可以针对这些功能采取的风险缓解计划,将一些需要引起注意的主题挑选为文章。 6.1 来自剪贴板的信息泄露风险 复制和粘贴是用户经常以不经意的方式使用的功能。 例如,不少用户使用这些功能来存储好奇或重要的信息,将邮件或网页中的东西记到记事本中,或者从存储密码的记事本复制并粘贴密码,以便不会提前忘记。 这些明显非常随意的行为,但实际上存在用户处理的信息可能被盗的隐藏风险。 这个风险与 Android 系统中的复制粘贴机制有关。 用户或应用复制的信息,曾经存储在称为剪贴板的缓冲区中。 存储在剪贴板中的信息,在被用户或应用粘贴时,分发给其他应用。 所以这个剪贴板功能中存在导致信息泄漏的风险。 这是因为剪贴板的实体在系统中是唯一的,并且任何应用都可以使用ClipboardManager,随时获取存储在剪贴板中的信息。 这意味着用户复制/剪切的所有信息都会泄露给恶意应用。 因此,考虑到 Android 操作系统的规范,应用开发人员需要采取措施,尽量减少信息泄露的可能性。 6.1.1 示例代码 粗略地说,有两种对策用于减轻来自剪贴板的信息泄露风险 从其他应用复制到你的应用时采取对策。 从你的应用复制到其他应用时采取对策。 首先,让我们讨论上面的对策(1)。 假设用户从其他应用(如记事本,Web 浏览器或邮件应用)复制字符串,然后将其粘贴到你的应用的EditText中。 事实证明,在这种情况下,基本没有对策,来防止由于复制和粘贴而导致的敏感信息泄漏。 由于 Android 中没有功能来控制第三方应用的复制操作。 因此,就对策(1)而言,除了向用户解释复制和粘贴敏感信息的风险外,没有任何方法,只能继续让用户自行减少操作。 接下来的讨论是上面的对策(2),假设用户复制应用中显示的敏感信息。 在这种情况下,防止泄漏的有效对策是,禁止来自视图(TextView,EditText等)的复制/剪切操作。 如果输入/输出敏感信息(如个人信息)的视图中,没有复制/剪切功能,信息泄漏永远不会通过剪贴板在你的应用发生。 有几种禁止复制/剪切的方法。 本节介绍简单有效的方法:一种方法是禁用视图的长按,另一种方法是在选择字符串时从菜单中删除复制/剪切条目。 对策的必要性可以根据图 6.1-1 的流程确定。 在图 6.1-1 中,“输入类型固定为密码属性”表示,输入类型在应用运行时必须是以下三种之一。 在这种情况下,由于默认禁止复制/剪切,因此不需要采取对策。 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD 以下小节使用每个示例代码详细介绍了对策。 6.1.1.1 选择字符串时,从菜单中删除复制/剪切条目 在 Android 3.0(API Level 11)之前不能使用TextView.setCustomSelectionActionMODECallback()方法。 在这种情况下,禁止复制/剪切的最简单方法是禁用视图的长按。 禁用视图的长按可以在layout.xml文件中规定。 下面展示了示例代码,用于从EditText中的字符串选择菜单中删除复制/剪切条目。 要点: 从字符串选择菜单中删除android.R.id.copy。 从字符串选择菜单中删除android.R.id.cut。 UncopyableActivity.java package org.jssec.android.clipboard.leakage; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; import android.widget.EditText; public class UncopyableActivity extends Activity { private EditText copyableEdit; private EditText uncopyableEdit; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.uncopyable); copyableEdit = (EditText) findViewById(R.id.copyable_edit); uncopyableEdit = (EditText) findViewById(R.id.uncopyable_edit); // By setCustomSelectionActionMODECallback method, // Possible to customize menu of character string selection. uncopyableEdit.setCustomSelectionActionModeCallback(actionModeCallback); } private ActionMode.Callback actionModeCallback = new ActionMode.Callback() { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } public void onDestroyActionMode(ActionMode mode) { } public boolean onCreateActionMode(ActionMode mode, Menu menu) { // *** POINT 1 *** Delete android.R.id.copy from the menu of character string selection. MenuItem itemCopy = menu.findItem(android.R.id.copy); if (itemCopy != null) { menu.removeItem(android.R.id.copy); } // *** POINT 2 *** Delete android.R.id.cut from the menu of character string selection. MenuItem itemCut = menu.findItem(android.R.id.cut); if (itemCut != null) { menu.removeItem(android.R.id.cut); } return true; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; } }; @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.uncopyable, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); } } 6.1.1.2 禁用视图的长按 禁止复制/剪切也可以通过禁用视图的长按来实现。 禁用视图的长按可以在layout.xml文件中规定。 要点: 在视图中将android:longClickable设置为false,来禁止复制/剪切。 unlongclickable.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/unlongclickable_description" /> <!-- EditText to prohibit copy/cut EditText --> <!-- *** POINT 1 *** Set false to android:longClickable in View to prohibit copy/cut. --> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:longClickable="false" android:hint="@string/unlongclickable_hint" /> </LinearLayout> 6.1.2 规则书 将敏感信息从你的应用复制到其他应用时,请遵循以下规则: 6.1.2.1 禁用视图中显示的复制/剪切字符串(必需) 如果应用中存在显示敏感信息的视图,并且允许在视图中像EditText一样复制/剪切信息,信息可能会通过剪贴板泄漏。 因此,必须在显示敏感信息的视图中禁用复制/剪切。 有两种方法禁用复制/剪切。 一种方法是从字符串选择菜单中删除复制/剪切条目,另一种方法是禁用视图的长按。 请参阅“6.1.3.1 应用规则时的注意事项”。 6.1.3 高级话题 6.1.3.1 应用规则时的注意事项 在TextView中,选择字符串是不可能的,因此通常不需要对策,但在某些情况下,可以复制取决于应用的规范。选择/复制字符串的可能性可以通过使用TextView.setTextIsSelectable()方法动态决定。将TextView设置为可以复制时,应调查在TextView中显示任何敏感信息的可能性,并且如果有任何可能性,则不应将其设置为可复制的。 另外,在“6.1.1 示例代码”的决策流程中描述,根据EditText的输入类型(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD等),假设输入类型是密码,通常不需要任何对策,因为复制字符串是默认禁止的。但是,如“5.1.2.2 提供以明文显示密码的选项(必需)”中所述,如果准备了【以明文显示密码】的选项,则在以明文显示密码的情况下,输入类型将会改变,并且启用复制/剪切。因此应该要求采取同样的对策。 请注意,开发者在应用规则时,还应考虑到应用的可用性。 例如,在用户可以自由输入文本的视图的情况下,如果因输入敏感信息的可能性很小而禁用了复制/剪切,用户可能会感到不便。 当然,该规则应该无条件地,应用于处理非常重要的信息或独立的敏感信息的视图,但在视图之外的情况下,以下问题将帮助开发人员了解如何正确处理视图。 准备一些专门用于敏感信息的其他组件 当向应用的粘贴是显而易见的时候,用其他方法发送信息 提醒用户注意输入/输出信息 重新审视视图的必要性 信息泄露风险的根源在于,Android 操作系统中剪贴板和剪贴板管理器的规范不考虑安全风险。 应用开发人员需要在用户完整性,可用性,功能等方面创建更高质量的应用。 6.1.3.2 存储在剪贴板中的操作信息 正如“6.1 来自剪贴板的信息泄漏风险”中所述,应用可以使用ClipboardManager,操作存储在剪贴板中的信息。另外,不需要为使用ClipboardManager设置特定的权限,因此应用可以在不被用户识别的情况下,使用ClipboardManager。 存储在剪贴板中的信息称为ClipData,可以通过ClipboardManager.getPrimaryClip()方法获得。如果通过ClipboardManager.addPrimaryClipChangedListener()方法,将侦听器注册到ClipboardManager,并实现了OnPrimaryClipChangedListener,则每次用户执行复制/剪切操作时都会调用监听器。因此可以在不忽略时间的情况下获得ClipData。在任何应用中执行复制/剪切操作时,都会调用监听器。 下面显示了服务的源代码,无论什么时候在设备中执行复制/剪切,它都会获取ClipData并通过Toast显示。你可以意识到,存储在剪贴板中的信息被泄露出来,就是由于下面的简单代码。有必要注意,敏感信息至少不会由以下源代码使用。 ClipboardListeningService.java package org.jssec.android.clipboard; import android.app.Service; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ClipboardManager.OnPrimaryClipChangedListener; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.util.Log; import android.widget.Toast; public class ClipboardListeningService extends Service { private static final String TAG = "ClipboardListeningService"; private ClipboardManager mClipboardManager; @Override public IBinder onBind(Intent arg0) { return null; } @Override public void onCreate() { super.onCreate(); mClipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); if (mClipboardManager != null) { mClipboardManager.addPrimaryClipChangedListener(clipListener); } else { Log.e(TAG, "Failed to get ClipboardService . Service is closed."); this.stopSelf(); } } @Override public void onDestroy() { super.onDestroy(); if (mClipboardManager != null) { mClipboardManager.removePrimaryClipChangedListener(clipListener); } } private OnPrimaryClipChangedListener clipListener = new OnPrimaryClipChangedListener() { public void onPrimaryClipChanged() { if (mClipboardManager != null && mClipboardManager.hasPrimaryClip()) { ClipData data = mClipboardManager.getPrimaryClip(); ClipData.Item item = data.getItemAt(0); Toast.makeText( getApplicationContext(), "Character stirng that is copied or cut:¥n" + item.coerceToText(getApplicationContext()), Toast.LENGTH_SHORT) .show(); } } }; } 接下来,下面显示了Activity的示例代码,它使用上面涉及的ClipboardListeningService。 ClipboardListeningActivity.java package org.jssec.android.clipboard; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; public class ClipboardListeningActivity extends Activity { private static final String TAG = "ClipboardListeningActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_clipboard_listening); } public void onClickStartService(View view) { if (view.getId() != R.id.start_service_button) { Log.w(TAG, "View ID is incorrect."); } else { ComponentName cn = startService( new Intent(ClipboardListeningActivity.this, ClipboardListeningService.class)); if (cn == null) { Log.e(TAG, "Failed to launch the service."); } } } public void onClickStopService(View view) { if (view.getId() != R.id.stop_service_button) { Log.w(TAG, "View ID is incorrect."); } else { stopService(new Intent(ClipboardListeningActivity.this, ClipboardListeningService.class)); } } } 到目前为止,我们已经介绍了获取存储在剪贴板上的数据的方法。 也可以使用ClipboardManager.setPrimaryClip()方法在剪贴板上存储新数据。 请注意,setPrimaryClip()方法将覆盖存储在剪贴板中的信息,因此用户的复制/剪切存储的信息可能会丢失。 当使用这些方法提供自定义复制/剪切功能时,必须按需设计/实现,以防止存储在剪贴板中的内容改变为意外内容,通过显示对话框来通知内容将被改变。

优秀的个人博客,低调大师

安卓应用安全指南 4.10 使用通知

4.10 使用通知 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 Android 提供用于向最终用户发送消息的通知功能。 使用通知会使一个称为状态栏的区域出现在屏幕上,你可以在其中显示图标和消息。 在 Android 5.0(API Level 21)中增强了通知的通信功能,即使在屏幕锁定时也可以通过通知显示消息,具体取决于用户和应用设置。 但是,不正确地使用通知,会导致私人信息(只应向最终用户自己显示)可能会被第三方看到。 出于这个原因,必须谨慎地注意隐私和安全性来实现此功能。 下表中总结了可见性选项的可能值和通知的相应行为。 可见性的值 通知行为 公共 通知会显示在所有锁定屏幕上 私有 通知显示在所有锁定的屏幕上;然而,在被密码保护的锁定屏幕上(安全锁),通知的标题和文本等字段是隐藏的(由公开可释放消息取代,私有信息是隐藏的) 秘密 通知不会显示在受密码或其他安全措施(安全锁)保护的锁定屏幕上。 (通知显示在不涉及安全锁的锁定屏幕上。) 4.10.1 示例代码 当通知包含有关最终用户的私人信息时,必须从中排除了私人信息,之后才能添加到锁定屏幕来显示。 下面展示了示例代码,说明了如何正确将通知用于包含私人数据的消息。 要点: 1) 将通知用于包含私人数据的消息,请准备适合公开显示的通知版本(屏幕锁定时显示)。 2) 不要在公开显示的通知中包含隐私信息(屏幕锁定时显示)。 3) 创建通知时将可见性显示设置为私有。 4) 当可见性设置为私有时,通知可能包含私人信息。 VisibilityPrivateNotificationActivity.java package org.jssec.notification.visibilityPrivate; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.view.View; public class VisibilityPrivateNotificationActivity extends Activity { /** * Display a private Notification */ private final int mNotificationId = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onSendNotificationClick(View view) { // *** POINT 1 *** When preparing a Notification that includes private information, prepare an additional Noficiation for public display (displayed when the screen is locked). Notification.Builder publicNotificationBuilder = new Notification.Builder(this).setContentTitle("Notif ication : Public"); if (Build.VERSION.SDK_INT >= 21) publicNotificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC); // *** POINT 2 *** Do not include private information in Notifications prepared for public display (displayed when the screen is locked). publicNotificationBuilder.setContentText("Visibility Public : Omitting sensitive data."); publicNotificationBuilder.setSmallIcon(R.drawable.ic_launcher); Notification publicNotification = publicNotificationBuilder.build(); // Construct a Notification that includes private information. Notification.Builder privateNotificationBuilder = new Notification.Builder(this).setContentTitle("Notification : Private"); // *** POINT 3 *** Explicitly set Visibility to Private when creating Notifications. if (Build.VERSION.SDK_INT >= 21) privateNotificationBuilder.setVisibility(Notification.VISIBILITY_PRIVATE); // *** POINT 4 *** When Visibility is set to Private, Notifications may contain private information. privateNotificationBuilder.setContentText("Visibility Private : Including user info."); privateNotificationBuilder.setSmallIcon(R.drawable.ic_launcher); // When creating a Notification with Visibility=Private, we also create and register a separate Notification with Visibility=Public for public display. if (Build.VERSION.SDK_INT >= 21) privateNotificationBuilder.setPublicVersion(publicNotification); Notification privateNotification = privateNotificationBuilder.build(); //Although not implemented in this sample code, in many cases //Notifications will use setContentIntent(PendingIntent intent) //to ensure that an Intent is transmission when Notification //is clicked. In this case, it is necessary to take steps--depending //on the type of component being called--to ensure that the Intent //in question is called by safe methods (for example, by explicitly //using Intent). For information on safe methods for calling various //types of component, see the following sections. //4.1. Creating and using Activities //4.2. Sending and receiving Broadcasts //4.4. Creating and using Services NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(mNotificationId, privateNotification); } } 4.10.2 规则书 创建通知时,应该遵循下列规则: 4.10.2.1 无论可见性设置如何,通知都不得包含敏感信息(尽管私有信息是例外情况)(必需) 在使用 Android 4.3(API 级别 18)或更高版本的终端上,用户可以使用“设置”窗口,授予应用读取通知的权限。 获得此权限的应用将能够读取通知中的所有信息;因此,通知中不得包含敏感信息。 (但是,根据“可见性”设置,通知中可能会包含私有信息)。 通知中包含的信息通常不会被发送通知的应用以外的应用读取。 但是,用户可以明确将权限授予某些用户选择的应用,来读取通知中的所有信息。 因为只有用户已授予权限的应用才能读取通知中的信息,所以在通知中包含用户的私有信息没有任何问题。 另一方面,如果在通知中包括除了用户的私有信息之外的敏感信息(例如,仅由应用开发者知道的秘密信息),则用户自己可以尝试读取通知中包含的信息,并且可以授予应用权限来查看这些信息;因此包含私有用户信息以外的敏感信息是有问题的。 特定方法和条件请见“4.10.3.1 用户授予的查看通知的权限”。 4.10.2.2 可见性为公共的通知,不能包含私有信息(必需) 在发送可见性为公共的通知时,私有用户信息不得包含在通知中。 当通知的可见性为公开时,即使屏幕被锁定,通知中的信息也会显示。 这是因为这种通知存在风险,私密信息可能被第三方物理邻近的终端看到和窃取。 VisibilityPrivateNotificationActivity.java // Prepare a Notification for public display (to be displayed on locked screens) that does not contain sensitive information Notification.Builder publicNotificationBuilder = new Notification.Builder(this).setContentTitle("Notification : Public"); publicNotificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC); // Do not include private information in Notifications for public display (to be displayed on locked screens) publicNotificationBuilder.setContentText("Visibility Public: sending notification without sensitive information "); publicNotificationBuilder.setSmallIcon(R.drawable.ic_launcher); 4.10.2.3 对于包含私有信息的通知,可见性必须显式设置为私有或秘密(必需) 即使屏幕锁定,使用 Android 5.0(API Level 21)或更高版本的终端也会显示通知。 因此,当通知包含私有信息时,其可见性标志应显式设置为私有或秘密。 这是为了防止通知中包含的私有信息显示在锁定屏幕上。 目前,可见性的默认值被设置为私有,所以前述风险只有在该标志显式变为公共时才会出现。 但是,可见性的默认值可能会在未来发生变化; 出于这个原因,并且为了在处理信息时始终清楚地表达意图,必须对包含私有信息的通知,将可见性显式设置为私有。 VisibilityPrivateNotificationActivity.java // Create a Notification that includes private information Notification.Builder priavteNotificationBuilder = new Notification.Builder(this).setContentTitle("Notification : Private"); // *** POINT *** Explicitly set Visibility=Private when creating the Notification priavteNotificationBuilder.setVisibility(Notification.VISIBILITY_PRIVATE); 私有信息的典型示例包括发送给用户的电子邮件,用户的位置数据,以及“5.5 处理隐私数据”部分列出的其他项目。 在使用 Android 4.3(API 级别 18)或更高版本的终端上,用户可以使用“设置”窗口,授予应用读取通知的权限,授予此权限的应用将能够读取通知中的所有信息;因此,除私有用户信息以外的敏感信息不得包含在通知中。 4.10.2.4 使用可见性为私有的通知,创建可见性为公共的额外通知用于展示(推荐) 当传递可见性为私有的信息时,最好同时创建一个额外的通知,用于公开展示,它的可见性为公开;这是为了限制锁定屏幕上显示的信息。 如果公开显示的通知未与可见性为私有的通知一起注册,则在屏幕锁定时将显示由操作系统准备的默认消息。 因此在这种情况下没有安全问题。 但是,为了在处理信息时始终清晰地表达意图,建议显示创建并注册公开显示的通知。 VisibilityPrivateNotificationActivity.java // Create a Notification that contains private information Notification.Builder privateNotificationBuilder = new Notification.Builder(this).setContentTitle("Notification : Private"); // *** POINT *** Explicitly set Visibility=Private when creating the Notification if (Build.VERSION.SDK_INT >= 21) privateNotificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC); // *** POINT *** Notifications with Visibility=Private may include private information privateNotificationBuilder.setContentText("Visibility Private : Including user info."); privateNotificationBuilder.setSmallIcon(R.drawable.ic_launcher); // When creating a Notification with Visibility=Private, simultaneously create and register a public-display Notification with Visibility=Public if (Build.VERSION.SDK_INT >= 21) privateNotificationBuilder.setPublicVersion(publicNotification); 4.10.3 高级话题 4.10.3 用户授予的查看通知的权限 如上面“4.10.2.1 无论可见性设置如何,通知不得包含敏感信息(尽管私人信息是例外)”所述,在使用 Android 4.3(API Level 18)或更高版本的终端上,某些用户选择的应用,已被授予用户权限,可能会读取所有通知中的信息。 但是,为了使应用有资格获得此用户权限,应用必须实现从NotificationListenerService派生的服务。 下面的代码展示了NotificationListenerService的用法。 AndroidManifest.xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jssec.notification.notificationListenerService"> <application android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <service android:name=".MyNotificationListenerService" android:label="@string/app_name" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> <intent-filter> <action android:name= "android.service.notification.NotificationListenerService" /> </intent-filter> </service> </application> </manifest> MyNotificationListenerService.java package org.jssec.notification.notificationListenerService; import android.app.Notification; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; public class MyNotificationListenerService extends NotificationListenerService { @Override public void onNotificationPosted(StatusBarNotification sbn) { // Notification is posted. outputNotificationData(sbn, "Notification Posted : "); } @Override public void onNotificationRemoved(StatusBarNotification sbn) { // Notification is deleted. outputNotificationData(sbn, "Notification Deleted : "); } private void outputNotificationData(StatusBarNotification sbn, String prefix) { Notification notification = sbn.getNotification(); int notificationID = sbn.getId(); String packageName = sbn.getPackageName(); long PostTime = sbn.getPostTime(); String message = prefix + "Visibility :" + notification.visibility + " ID : " + notificationID; message += " Package : " + packageName + " PostTime : " + PostTime; Log.d("NotificationListen", message); } } 如上所述,通过使用NotificationListenerService获取用户权限,可以读取通知。 但是,由于通知中终端上包含的信息经常包含私有信息,因此在处理此类信息时需要小心。

优秀的个人博客,低调大师

安卓应用安全指南 4.9 使用`WebView`

4.9 使用WebView 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 WebView使你的应用能够集成 HTML / JavaScript 内容。 4.9.1 示例代码 我们需要采取适当的行动,具体取决于我们想通过WebView展示的内容,尽管我们可以通过它轻松展示网站和 html 文件。 而且我们还需要考虑来自WebView卓越功能的风险; 如 JavaScript-Java 对象绑定。 我们特别需要关注 JavaScript。 (请注意 JavaScript 默认是禁用的,我们可以通过WebSettings#setJavaScriptEnabled()来启用它。 启用 JavaScript 存在潜在的风险,即恶意第三方可以获取设备信息并操作设备。 以下是使用WebView [19] 的应用的原则: [19] 严格地说,如果我们可以说内容是安全的,你可以启用 JavaScript。 如果内容是在内部管理的,则内容应该保证安全。 公司可以保护他们。 换句话说,我们需要让企业代表的决策,来为其他公司的内容启用 JavaScript。 由可信伙伴开发的内容可能会有安全保证。 但仍有潜在风险。 因此,负责人需要作出决定。 (1)如果应用使用内部管理的内容,则可以启用 JavaScript。 (2)除上述情况外,你不应启用 JavaScript。 图 4.9-1 显示了根据内容特征选择示例代码的流程图。 4.9.1.1 仅显示存储在 APK 中的assets / res目录下的内容 如果你的应用仅显示存储在 apk 中assets/和res/目录下的内容,则可以启用 JavaScript。 以下示例代码展示了,如何使用WebView显示存储在assets/和res/下的内容。 要点: 1) 禁止访问文件(apk 文件中的assets/和res/下的文件除外)。 2) 你可以启用 JavaScript。 WebViewAssetsActivity.java package org.jssec.webview.assets; import android.app.Activity; import android.os.Bundle; import android.webkit.WebSettings; import android.webkit.WebView; public class WebViewAssetsActivity extends Activity { /** * Show contents in assets */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = (WebView) findViewById(R.id.webView); WebSettings webSettings = webView.getSettings(); // *** POINT 1 *** Disable to access files (except files under assets/ and res/ in this apk) webSettings.setAllowFileAccess(false); // *** POINT 2 *** Enable JavaScript (Optional) webSettings.setJavaScriptEnabled(true); // Show contents which were stored under assets/ in this apk webView.loadUrl("file:///android_asset/sample/index.html"); } } 4.9.1.2 仅显示内部管理的内容 只有当你的网络服务和你的 Android 应用可以采取适当措施来保护它们时,你才可以启用 JavaScript 来仅仅显示内部管理的内容。 Web 服务端操作:如图 4.9-2 所示,你的 Web 服务只能引用内部管理的内容。 另外,Web 服务需要采取适当的安全措施。 因为你的网络服务涉及的内容可能存在风险,因此存在潜在风险;如恶意攻击代码注入,数据操作等。请参阅“4.9.2.1 仅在内容由内部管理时启用 JavaScript(必需)”。 Android 应用端操作:使用 HTTPS,应用只有在证书可信的情况下,才应与受管理的 Web 服务建立网络连接。 以下示例代码是一个活动,展示了内部管理的内容。 要点: 1) 适当处理来自WebView的 SSL 错误。 2) (可选)启用WebView的 JavaScript。 3) 将 URL 限制为 HTTPS 协议。 4) 将 URL 限制在内部。 WebViewTrustedContentsActivity.java package org.jssec.webview.trustedcontents; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Bundle; import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; import java.text.SimpleDateFormat; public class WebViewTrustedContentsActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); WebView webView = (WebView) findViewById(R.id.webView); webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { // *** POINT 1 *** Handle SSL error from WebView appropriately // Show SSL error dialog. AlertDialog dialog = createSslErrorDialog(error); dialog.show(); // *** POINT 1 *** Handle SSL error from WebView appropriately // Abort connection in case of SSL error // Since, there may be some defects in a certificate like expiration of validity, // or it may be man-in-the-middle attack. handler.cancel(); } }); // *** POINT 2 *** Enable JavaScript (optional) // in case to show contents which are managed in house. webView.getSettings().setJavaScriptEnabled(true); // *** POINT 3 *** Restrict URLs to HTTPS protocol only // *** POINT 4 *** Restrict URLs to in-house webView.loadUrl("https://url.to.your.contents/"); } private AlertDialog createSslErrorDialog(SslError error) { // Error message to show in this dialog String errorMsg = createErrorMessage(error); // Handler for OK button DialogInterface.OnClickListener onClickOk = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setResult(RESULT_OK); } }; // Create a dialog AlertDialog dialog = new AlertDialog.Builder( WebViewTrustedContentsActivity.this).setTitle("SSL connection error") .setMessage(errorMsg).setPositiveButton("OK", onClickOk) .create(); return dialog; } private String createErrorMessage(SslError error) { SslCertificate cert = error.getCertificate(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); StringBuilder result = new StringBuilder() .append("The site's certification is NOT valid. Connection was disconnected.¥n¥nError:¥n"); switch (error.getPrimaryError()) { case SslError.SSL_EXPIRED: result.append("The certificate is no longer valid.¥n¥nThe expiration date is ") .append(dateFormat.format(cert.getValidNotAfterDate())); return result.toString(); case SslError.SSL_IDMISMATCH: result.append("Host name doesn't match. ¥n¥nCN=") .append(cert.getIssuedTo().getCName()); return result.toString(); case SslError.SSL_NOTYETVALID: result.append("The certificate isn't valid yet.¥n¥nIt will be valid from ") .append(dateFormat.format(cert.getValidNotBeforeDate())); return result.toString(); case SslError.SSL_UNTRUSTED: result.append("Certificate Authority which issued the certificate is not reliable.¥n¥nCertificate Authority¥n") .append(cert.getIssuedBy().getDName()); return result.toString(); default: result.append("Unknown error occured. "); return result.toString(); } } } 4.9.1.3 显示非内部管理的内容 如果你的应用显示的内容不在内部管理,请勿启用 JavaScript,因为存在访问恶意内容的潜在风险。 以下示例代码是显示非内部管理的内容的活动。 此示例代码显示由用户通过地址栏输入的 URL 指定的内容。 请注意,当 JavaScript 错误发生时,JavaScript 被禁用并且连接中止。 HTTPS 通信的详细信息,错误处理与“4.9.1.2 仅显示内部管理的内容”相同。 详细信息请参阅“5.4 通过 HTTPS 进行通信”。 要点: 1) 适当处理来自WebView的 SSL 错误。 2) 禁用WebView的 JavaScript。 WebViewUntrustActivity.java package org.jssec.webview.untrust; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.graphics.Bitmap; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Bundle; import android.view.View; import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.EditText; import java.text.SimpleDateFormat; public class WebViewUntrustActivity extends Activity { /* * Show contents which are NOT managed in-house (Sample program works as a simple browser) */ private EditText textUrl; private Button buttonGo; private WebView webView; // Activity definition to handle any URL request private class WebViewUnlimitedClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView webView, String url) { webView.loadUrl(url); textUrl.setText(url); return true; } // Start reading Web page @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { buttonGo.setEnabled(false); textUrl.setText(url); } // Show SSL error dialog // And abort connection. @Override public void onReceivedSslError(WebView webview, SslErrorHandler handler, SslError error) { // *** POINT 1 *** Handle SSL error from WebView appropriately AlertDialog errorDialog = createSslErrorDialog(error); errorDialog.show(); handler.cancel(); textUrl.setText(webview.getUrl()); buttonGo.setEnabled(true); } // After loading Web page, show the URL in EditText. @Override public void onPageFinished(WebView webview, String url) { textUrl.setText(url); buttonGo.setEnabled(true); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); webView = (WebView) findViewById(R.id.webview); webView.setWebViewClient(new WebViewUnlimitedClient()); // *** POINT 2 *** Disable JavaScript of WebView // Explicitly disable JavaScript even though it is disabled by default. webView.getSettings().setJavaScriptEnabled(false); webView.loadUrl(getString(R.string.texturl)); textUrl = (EditText) findViewById(R.id.texturl); buttonGo = (Button) findViewById(R.id.go); } public void onClickButtonGo(View v) { webView.loadUrl(textUrl.getText().toString()); } private AlertDialog createSslErrorDialog(SslError error) { // Error message to show in this dialog String errorMsg = createErrorMessage(error); // Handler for OK button DialogInterface.OnClickListener onClickOk = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setResult(RESULT_OK); } }; // Create a dialog AlertDialog dialog = new AlertDialog.Builder( WebViewUntrustActivity.this).setTitle("SSL connection error") .setMessage(errorMsg).setPositiveButton("OK", onClickOk) .create(); return dialog; } private String createErrorMessage(SslError error) { SslCertificate cert = error.getCertificate(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); StringBuilder result = new StringBuilder() .append("The site's certification is NOT valid. Connection was disconnected.¥n¥nError:¥n"); switch (error.getPrimaryError()) { case SslError.SSL_EXPIRED: result.append("The certificate is no longer valid.¥n¥nThe expiration date is ") .append(dateFormat.format(cert.getValidNotAfterDate())); return result.toString(); case SslError.SSL_IDMISMATCH: result.append("Host name doesn't match. ¥n¥nCN=") .append(cert.getIssuedTo().getCName()); return result.toString(); case SslError.SSL_NOTYETVALID: result.append("The certificate isn't valid yet.¥n¥nIt will be valid from ") .append(dateFormat.format(cert.getValidNotBeforeDate())); return result.toString(); case SslError.SSL_UNTRUSTED: result.append("Certificate Authority which issued the certificate is not reliable.¥n¥nCertificate Authority¥n") .append(cert.getIssuedBy().getDName()); return result.toString(); default: result.append("Unknown error occured. "); return result.toString(); } } } 4.9.2 规则书 当你需要时候WebView时,遵循下列规则: 4.9.2.1 只在内容由内部管理时启用 JavaScript(必需) 对于WebView我们需要关注的是是否启用 JavaScript。 原则上,只有当应用访问内部管理的服务时,我们才能启用 JavaScript。 如果有可能访问非内部管理的服务,则不得启用 JavaScript。 内部管理的服务 如果应用访问内部开发的内容,并通过内部管理的服务器分发,我们可以说这些内容仅由贵公司修改。 另外,每个内容还需要仅仅引用存储在服务器中的内容,它们具有适当安全性。 在这种情况下,我们可以在WebView上启用 JavaScript。 请参阅“4.9.1.2 仅显示内部管理的内容”。 如果你的应用仅显示存储在 apk 中assets/和res/目录下的内容,你也可以启用 JavaScript。 请参阅“4.9.1.1 仅显示存储在assets / res目录下的内容”。 非内部管理的服务 你绝不能认为,你可以确保非内部管理的内容的安全性。 因此你必须禁用 JavaScript。 请参阅“4.9.1.3 显示非内部管理的内容”。 另外,如果内容存储在外部存储介质中,如 microSD,则必须禁用 JavaScript;因为其他应用可以修改内容。 4.9.2.2 使用 HTTPS 与内部管理的服务器进行通信(必需) 你必须使用 HTTPS 与内部管理的服务器通信,因为存在恶意第三方欺骗服务的潜在风险。 请参阅“4.9.2.4 正确处理 SSL 错误(必需)”和“5.4 通过 HTTPS 通信”。 4.9.2.3 禁用 JavaScript 来显示通过意图接收的 URL(必需) 如果你的应用需要显示从其他应用,以意图等形式传递的 URL,则不要启用 JavaScript。因为存在用恶意 JavaScript 显示恶意网页的潜在风险。 “4.9.1.2 仅显示内部管理的内容”部分中的示例代码,使用固定值 URL 显示内部管理的内容来确保安全。 如果你需要显示从意图收到的 URL,则必须确认该 URL 在内部管理的 URL 中。 简而言之,应用必须使用正则表达式等白名单来检查 URL。另外,它应该是 HTTPS。 4.9.2.4 适当处理 SSL 错误(必需) 当 HTTPS 通信发生 SSL 错误时,你必须终止网络通信并通知用户错误。 SSL 错误显示了无效的服务器认证风险或 MTIM(中间人攻击)风险。 请注意,WebView没有 SSL 错误的错误通知机制。 因此,你的应用必须显示错误通知,来向用户通知风险。 请参阅“4.9.1.2 仅显示内部管理的内容”和“4.9.1.3 显示非内部管理的内容”一节中的示例代码。 另外,你的应用必须终止带有错误通知的通信。 换句话说,你不可以这样做。 忽略错误来与服务保持通信。 重试 HTTP 通信而不是 HTTPS。 请参阅“5.4 通过 HTTPS 进行通信”中所述的详细信息。 WebView的默认行为是,发生 SSL 错误时终止通信。 因此,我们需要添加显示 SSL 错误通知。 然后我们可以正确处理 SSL 错误。 4.9.3 高级话题 4.9.3.1 Android 4.1 或更低版本中由addJavascriptInterface()引起的漏洞 4.2(API Level 17)版本以下的 Android,具有由addJavascriptInterface()引起的漏洞,这可能允许攻击者通过WebView上的 JavaScript 调用 Android 本地方法(Java)。 正如“4.9.2.1 只在内容由内部管理时启用 JavaScript”中所述,如果服务可以访问内部控制之外的服务,则不得启用 JavaScript。 在 Android 4.2(API Level 17)或更高版本中,已采取措施,将漏洞限制为在 Java 源代码上使用@JavascriptInterface注释的方法,而不是所有注入的 Java 对象的方法。 但是,如果服务可以访问内部控制之外的服务,则必须禁用JavaScript,像“4.9.2.1”中提到的那样。 4.9.3.2 由文件模式导致的问题 如果使用默认设置的WebView,应用具有访问权限的所有文件,都可以通过在网页中通过文件模式访问,而无论页面的来源如何。 例如,恶意网页可以通过使用文件模式,向应用的私有文件的 URI 发送请求,来访问存储在应用私有目录中的文件。 如果服务可以访问内部控制之外的服务,则禁用 JavaScript 的方法如“4.9.2.1 只在内容由内部管理时启用 JavaScript(必需)”中所述。 这样做是为了防止发送恶意文件模式请求。 同样在 Android 4.1(API Level 16)或更高版本的情况下,可以使用setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()来限制通过文件模式的访问。 禁用文件模式 webView = (WebView) findViewById(R.id.webview); webView.setWebViewClient(new WebViewUnlimitedClient()); WebSettings settings = webView.getSettings(); settings.setAllowUniversalAccessFromFileURLs(false); settings.setAllowFileAccessFromFileURLs(false); 4.9.3.3 使用 Web 消息时指定发送者的来源 Android 6.0(API Level 23)增加了一个 API,用于实现 HTML5 Web 消息传送。 Web 消息传送是一种在 HTML5 中定义的框架,用于在不同的浏览上下文之间,发送和接收数据 [20]。添加到WebView类的postWebMessage()方法是一种方法,通过 Web 消息传送定义的跨域消息传送协议处理数据传输。 [20] http://www.w3.org/TR/webmessaging/ 此方法从WebView已读入的浏览上下文中发送一个消息,该消息由其第一个参数指定; 然而,在这种情况下,有必要指定发送者的来源作为第二个参数。 如果指定的来源 [21] 与发送者上下文中的来源不符,则不会发送该消息。 通过以这种方式限制发送者来源,此机制旨在防止消息传递给非预期发送者。 [21] “来源”是一个 URL 模式以及一个主机名和端口号。 详细定义请参阅 http://tools.ietf.org/html/rfc6454。 但是,重要的是要注意,通配符可能被指定为postWebMessage()方法中的来源 [22]。如果指定了通配符,则不会检查消息的发送者来源,并且可以从任意来源发送消息。 在恶意内容已被读入WebView的情况下,如果发送重要消息时没有来源限制,则可能导致各种类型的损害。 因此,在使用WebView进行 Web 消息传递时,最好在postWebMessage()方法中明确指定特定的源。 [22] 请注意,通配符是Uri.EMPTY和Uri.parse("")(在编写 2016 年 9 月 1 日的版本时)。

优秀的个人博客,低调大师

Python之调用安卓(Android)ADB指令

作为开发或者是测试人员,或者是使用Python的伙伴,都会接触到ADB指令,下面就针对这种情况,来解析下Python调用ADB指令的操作。 一、长时间按某个元素:adb input shell ... 语法:adb -s 4d0041be98b01f shell input touchscreen swipe 540 716 545 718 1000 语义:-s 后跟设备号,swipe 先传移动坐标范围‘540 716 545 718’,然后1000是长按时间,单位毫秒。 实现代码 二、adb 命令输入中文: adb -s 设备号 shell input text 输入的内容 语法:adb shell am broadcast -a ADB_INPUT_TEXT --es msg "内容" 语义:利用“ADBKeyBoard”输入法来进行中文输入,通过广播的方式达到输入中文字符 三、adb 启动应用 语法:adb shell am start -n packgage名 /.activity 如启动计算器程序:adb shell am start -n com.android.calculator2/.Calculator 提醒点:一定要找到、找对activity和package 四、adb 查询短信、发短信消息 发短信语法:adb shell am start -a android.intent.action.SENDTO -d smsto:发送号码 --es sms_body 短信内容 查询短信消息:1.手机需ROOT;2.需找到短信数据库文件 数据库文件路径:'/data/data/com.android.providers.telephony/databases/mmssms.db',其中‘mmssms.db’文件就是保存短信内容的数据库文件,可利用正则表达式匹配。 说了这么多ADB指令,可发现adb指令是逐条运行的,那再Python中是一次性完成所有的步骤的,如下指令 语法:首先要确认设备是否已经ROOT ,指令:adb shell su -c cd 'data\system' 语义:-c 后面跟着具体的操作指令即可。 ADB

优秀的个人博客,低调大师

安卓App和java通信实例

服务器:放在电脑上运行的java文件 import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class MyServer implements Runnable{ //服务器连接 public static ServerSocket serverSocket; //连接 public static Socket socket; //端口 public static final int PORT = 8888; public void run() { DataInputStream dis = null; DataOutputStream dos = null; try { serverSocket = new ServerSocket(PORT); System.out.println("正在等待客户端连接..."); //这里处于等待状态,如果没有客户端连接,程序不会向下执行 while(true){ socket = serverSocket.accept(); dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); //读取数据 String clientStr = dis.readUTF(); //写出数据 dos.writeUTF(clientStr); System.out.println("----客户端已成功连接!----"); //得到客户端的IP System.out.println("客户端的IP =" + socket.getInetAddress()); //得到客户端的端口号 System.out.println("客户端的端口号 =" + socket.getPort()); //得到本地端口号 System.out.println("本地服务器端口号=" + socket.getLocalPort()); System.out.println("-----------------------"); System.out.println("客户端:" + clientStr); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally {//我们把流的关闭写在finally里,即使读写出现问题,我们也能正常的关闭流! try { if (dis != null) dis.close(); if (dos != null) dos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args){ Thread desktopServerThread = new Thread(new MyServer()); desktopServerThread.start(); } } App工程文件: 1、AndroidManifest.xml(主xml文件) <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.himi" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="android.permission.INTERNET" /> </manifest> 2、res/values/strings.xml(资源文件) <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">这里输入文字发给服务器</string> <string name="app_name">SocketConnect</string> <string name="send">发送</string> <string name="get">这里显示服务器发来的信息!</string> </resources> 3、res/layout/main.xml(资源文件) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <EditText android:id="@+id/edit" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/Btn_commit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send" /> <TextView android:layout_width="fill_parent" android:id="@+id/tv" android:layout_height="wrap_content" android:text="@string/get" /> </LinearLayout> 4、src/com.himi/MainActivity.java(Activity文件,客户端端主程序) package com.himi; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; /** * @author Himi */ public class MainActivity extends Activity implements OnClickListener { private Button btn_ok; private EditText edit; private TextView tv; //Socket用于连接服务器获取输入输出流 private Socket socket; //服务器server/IP地址 private final String ADDRESS = "10.203.8.167"; //服务器端口 private final int PORT = 8888; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); btn_ok = (Button) findViewById(R.id.Btn_commit); tv = (TextView) findViewById(R.id.tv); edit = (EditText) findViewById(R.id.edit); btn_ok.setOnClickListener(this); } public void onClick(View v) { if (v == btn_ok) { DataInputStream dis = null; DataOutputStream dos = null; try { //阻塞函数,正常连接后才会向下继续执行 socket = new Socket(ADDRESS, PORT); //socket = new Socket("localhost", PORT); dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); //向服务器写数据 dos.writeUTF(edit.getText().toString()); String temp = "I say:"; temp += edit.getText().toString(); temp += "\n"; temp += "Server say:"; //读取服务器发来的数据 temp += dis.readUTF(); tv.setText(temp); } catch (IOException e) { Log.e("Himi", "Stream error!"); e.printStackTrace(); } finally { try { if (dis != null) dis.close(); if (dos != null) dos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } 本文转自beautifulzzzz博客园博客,原文链接: http://www.cnblogs.com/zjutlitao/p/3477529.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

新漏洞发现,影响 95% 安卓设备

据国外媒体报道,网络安全机构Zimperium周一表示,研究人员已发现一批能影响全球95%的Android设备的系统漏洞。通过这些漏洞,黑客将可以在不被发现的情况下远程访问设备数据。目前,已确认从2.2到5.1版达Android系统均存在漏洞。 漏洞出自Android系统内一个被称为Stagefright的多媒体库,该媒体库中包含了多个高危系统漏洞。Zimperium已计划将在今年8月举行的“黑帽安全大会”上对公众展示其具体研究成果。 利用这些漏洞发起攻击,黑客实际上只需要获得目标用户的电话号码,并通过短信发送一个媒体文件即可。此过程将赋予黑客远程接入设备的可能,随后在用户入睡后,黑客可进一步对设备上传木马,以窃取更多信息并抹除证据。 “这 些漏洞极其危险,因为它们并不需要受害者做出任何操作就可以被利用。这与钓鱼等行为不同,后者需要打开PDF文件或者某个链接才会导致系统被感染,而前者 在你睡觉时就自然而然地被利用和发起攻击。而在你醒来以后,所有的攻击罪证都已经被抹除,你不会知道手机已感染木马,你会像往常一样继续使用着你的电 话。”Zimperium首席技术官Zuk Avraham表示。 虽然谷歌已经通过Android开源项目发布了修复补丁,但Zimperium建议Android用户应该主动联系运营商和设备生产商以确保尽早获得系统升级。 谷歌在周一晚些时候就此事发布了声明,强调公司一向注重网络安全的价值观,并已经向所有合作商提供了针对漏洞的修补方案。此外,谷歌还指出,Android设备使用了沙盒技术,因此这在保护用户数据方面能起到至关重要的作用。 文章转载自 开源中国社区 [http://www.oschina.net]

优秀的个人博客,低调大师

早报:安卓 4.1.1 用户面临心血风险

据国外媒体报道,OpenSSL曝出“心脏流血”(Heartbleed)安全漏洞已有一个星期。尽管不少企业已为该漏洞发布补丁修复,但仍有像是运行Android 4.1.1系统这样数亿计的庞大手机用户群体面临着“心脏流血”的风险。 OpenSSL于4月7日公布了“心脏流血”漏洞,同天也为该漏洞给出了修复补丁。然而,运行Android 4.1.1操作系统的平板电脑和智能手机仍有数亿台,这些设备仍然曝露在OpenSSL的漏洞之下。根据谷歌公布的数据,于2012年中期发布的Android 4.1 Jellybean系统,目前仍占到其9亿Android用户的三分之一以上。 虽然谷歌强调只有不足10%的激活设备容易受到“心脏流血”漏洞的攻击——这仍然意味着有成百上千万用户眼下正处于无保护状态。 “设备生产商及运营商们现在需要做些什么以避免漏洞被利用,但这一过程通常是漫长的。”安全机构Lacoon Security联合创始人兼CEO迈克尔·邵洛夫(Michael Shaulov)表示。 截至目前,黑客的进攻目标基本为使用OpenSSL协议的服务器站点,还没有任何个人终端设备被列为目标——因为发起这样的攻击需要进行较繁重的工作,而成果则只能是获得某一部设备上的单个用户的信息。 但不管怎样,填补漏洞避免被攻击仍然是最首要的任务。譬如黑莓就已明确表示要为其Android版及iOS版BBM应用和Secure Work Space企业邮件应用提供“心脏流血”补丁。 文章转载自开源中国社区 [http://www.oschina.net]

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册